三剑客

编译,顾名思义,就是源代码执行前会经历的过程,分三个步骤,

  • 分词/词法分析,将我们写的代码字符串分解成多个词法单元
  • 解析/语法分析,将词法单元集合生成抽象语法树(AST)
  • 代码生成,抽象语法树(AST)转换成可执行代码的过程

Tip1:js在语法分析和代码生成阶段有对运行性能进行优化,对冗余元素进行优化

Tip2:js的编译过程不是发生在构建之前,而是代码执行之前

理解作用域,首先知道三剑客,分别是

  • 引擎:负责整个代码编译及执行的过程
  • 编译器: 负责词法分析、语法分析、代码生成
  • 作用域:负责维护与收集所有声明的标识符,保证当前执行代码对这些标识符的访问权限

举例子,加深印象,对于var a = 2,三剑客如何协同工作,

编译器进行分词、语法分析,然后要代码生成时,遇到 var a,问一下当前作用域集合“你有没有这个名称的变量呀?”,作用域如果说有,那么忽略声明,继续编译,如果说没有,那么就要要求作用域收集一下,并且给它个名字a,然后编译器就生成了代码,引擎准备来执行了,先问当前作用域集合,“你这里有a这个变量吗?”,有引擎就拿来用,没有就继续找该变量,要么找到,就给它附个值2,没有那就给你报个错!

理解编译器的相关术语

  • LSH查询,通俗解释就是找到所声明变量,并且对其赋值的行为
  • RSH查询,通俗解释就是查找声明的变量

作用域嵌套

当一个块或是函数嵌套在另外一个块或函数时,就会产生作用域嵌套,于是在当前作用域找不到某个变量时,引擎会往外层嵌套作用域继续查找,直达到最外层作用域(全局作用域)为止,也就是所谓的作用域链啦!

词法作用域

相信有很多人都是搞不懂词法作用域是什么?

所谓的词法作用域,就是定义在词法阶段(词法分析)的作用域,由你写代码时将变量和块作用域写在哪里来决定的。

遮蔽效应

作用域查找会在找到第一个匹配的标识符时停止,不会继续往上层作用域查找,这就会产生遮蔽效应

欺骗词法作用域

  • eval函数,修改词法作用域
  • with关键字,创建词法作用域

导致性能下降的原因,前面我们提过,在解析/语法分析、生成代码阶段,我们会对代码进行优化,剔除冗余元素,但是当使用eval/with时,所有优化变得没有意义,因为存在不可预见性,不知道修改和创建的词法作用域是什么?所以会导致性能下降。

函数声明、函数表达式、匿名函数表达式

函数表达式:function为第一个词,那么就是一个函数声明,否则就是一个函数表达式

匿名函数表达式:没有函数名,匿名函数在栈追踪这种不会显示有意义的函数名,使得调试困难

IIFE: 立即执行函数表达式((function() { ... }())(function(){ ... })()

块级作用域

let为其声明的变量隐式地去劫持了所在的块级作用域,不会在块级作用域中进行提升【变量提升】

Demo: with关键字为块级作用域、{...}为块级作用域,用完即销毁

const常量,不可修改!

任何声明在某个作用域(函数作用域和块级作用域)的变量,都是属于这个作用域。

每个作用域都会进行提升操作。

函数声明会被提升,函数表达式不会提升,变量声明提升的过程中,函数会优先!

闭包

闭包,有权访问另外一个函数的变量标识符的函数,比较常见的一个闭包问题,就是for循环。

    for(var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i*1000)
}

会发现每一次输出的都是6,为啥勒?所有的回调函数回在循环结束后才会执行(事件循环)。所以每次都是输出一个6来。

解决办法:

1、IIFE,每个迭代储存i的值

    for(var i = 1; i <= 5; i++) {
(function(i) {
var j = i;
setTimeout(function() {
console.log(j);
}, j*1000);
})(i)
}

2、IIFE,将i入参修改成j

    for(var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, j*1000);
})(i)
}

3、创建闭包的块作用域

    for(var i = 1; i <= 5; i++) {
let j = i;
setTimeout(function() {
console.log(j);
}, j*1000);
}

4、最优

    for(let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i*1000)
}

另外一个闭包常用的场景,就是模块暴露了,在这里提供一个现代模块机制的实现方式,大家可以细细品尝,

var MyMoudles = (function Manger() {
var modules = {};
function define(name, deps, impl) {
for (var i = 0; i < deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply(impl, deps);
}
function get(name) {
return modules[name];
}
})()

调用Demo:

MyMoudles.define('bar', [], function() {
function hello(who) {
return "Let me introduce: " + who;
}
return {
hello: hello
}
})
MyMoudles.define('foo', ['bar'], function(bar) {
var hungry = 'hippo';
function awesome() {
console.log(bar.hello(hungry).toUpperCase());
}
return {
awesome: awesome
}
})
var bar = MyMoudles.get('bar');
var foo = MyMoudles.get('foo'); console.log(bar.hello('hippo')); // Let me introduce: hippo
foo.awesome();

未来模块机制

ES6提供了全新的模块机制,基于函数的模块(如上述现代模块机制)并不是一个能被静态识别的模式(编译器无法识别),它们的API语义只有等到代码运行时才会考虑进来,而ES6模块就是一个能被静态识别的模式,就是说API在编译阶段就会检查API成员是否存在。

原文地址

你不知道的javascript(上卷)读后感(一)的更多相关文章

  1. 你不知道的javascript(上卷)读后感(二)

    this词法 熟悉ES6语法的开发者,箭头函数在涉及this绑定时的行为和普通函数的行为完全不一致.跟普通this绑定规则不一样,它使用了当前的词法作用域覆盖了this本来的值. 误解 this理解成 ...

  2. 你不知道的JavaScript上卷笔记

    你不知道的JavaScript上卷笔记 前言 You don't know JavaScript是github上一个系列文章   初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目 ...

  3. 读《你不知道的JavaScript(上卷)》后感-作用域闭包(二)

    github原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们, ...

  4. 你不知道的javaScript上卷(第一章 作用域是什么)

    在写这篇博客时这本书我已经是看过一遍了,为了加深印象和深入学习于是打算做这系列的前端经典书籍导读博文,大家如果觉得这本书讲的好可以自己买来看看,我是比较喜欢看纸质版书的,因为这样才有读书的那种感觉. ...

  5. 读《你不知道的JavaScript(上卷)》后感-浅谈JavaScript作用域(一)

    原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们,也入手看看这 ...

  6. 《你不知道的 JavaScript 上卷》 学习笔记

    第一部分: 作用域和闭包 一.作用域 1. 作用域:存储变量并且查找变量的规则 2. 源代码在执行之前(编译)会经历三个步骤: 分词/此法分析:将代码字符串分解成有意义的代码块(词法单元) 解析/语法 ...

  7. 【你不知道的javaScript 上卷 笔记3】javaScript中的声明提升表现

    console.log( a ); var a = 2; 执行输出undefined a = 2; var a; console.log( a ); 执行输出2 说明:javaScript 运行时在编 ...

  8. JS闭包—你不知道的JavaScript上卷读书笔记(二)

    关于闭包,初学者会被绕的晕头转向,在学习的路上也付出了很多精力来理解. 让我们一起来揭开闭包神秘的面纱. 闭包晦涩的定义 看过很多关于闭包的定义,很多讲的云里雾里,晦涩难懂.让不少人以为闭包是多么玄乎 ...

  9. JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)

    前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...

随机推荐

  1. ubuntu16.04 TLS安装java

    apt-get install software-properties-commonadd-apt-repository ppa:webupd8team/javaapt-get updateapt-g ...

  2. Python3之多重继承

    继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能. Animal类层次的设计,假设我们要实现以下4中动物 Dog-狗狗 Bat-蝙蝠 Parrot-鹦鹉 Ostrich-鸵鸟 ...

  3. C/C++查漏补缺(常更)

    一.#define宏定义 如下程序段,则输出结果为: #define MAX 12 int main(){ cout << "20\0MAX019" << ...

  4. Linux利器之perf(火焰图)

    1 Ubuntu下安装perf 在Ubuntu18上默认没有安装perf,可以通过如下命令尝试:sudo apt-get install linux-tools 结果出来以下内容: Reading p ...

  5. Asp.Net Core中的角色

    在前面介绍中我们知道了Asp.Net Core Identity中创建用户使用到的类UserManager<IdentityUser>,同样的,创建角色我们需要使用RoleManager& ...

  6. RAID(独立磁盘冗余阵列)

    Raid ​ RAID 独立磁盘冗余阵列,在本科学习时候学习过,记不清是组成原理还是操作系统,当时理解的不太清楚,现在研究生期间做存储相关项目,涉及到了Raid,于是查各种博客,为了以后便于后期查阅, ...

  7. 转:SLAM算法解析:抓住视觉SLAM难点,了解技术发展大趋势

    SLAM(Simultaneous Localization and Mapping)是业界公认视觉领域空间定位技术的前沿方向,中文译名为“同步定位与地图构建”,它主要用于解决机器人在未知环境运动时的 ...

  8. SAS学习笔记2 基础函数应用

    输入输出语句(put和input函数) put()函数:把数值型或字符型变量转为字符型变量(输出变量) input()函数:将字符型变量转化为数值型变量(输入变量) 选择与删除语句(keep.drop ...

  9. Spring框架ioc概括

    什么是Spring且能做什么 Spring是一个开源框架,它由Rod Johnson创建.它是为了解决企业应用开发的复杂性而创建的. Spring使用基本的JavaBean来完成以前只可能由EJB完成 ...

  10. 数据库设计_ERMaster安装使用_PowerDesigner数据设计工具

    数据库设计 1. 说在前面 项目开发的流程包括哪些环节 需求调研[需求调研报告]-- 公司决策层 (1) 根据市场公司需求分析公司是否需要开发软件来辅助日常工作 (2) 公司高层市场考察,市场分析,决 ...