你不知道的JavaScript 上卷 2/11
第一部分——作用域和闭包
第一章
作用域是什么
1.几乎所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且能在之后对这个值进行访问或修改。事实上,正是这种储存和访问变量的值的能力将状态带给了程序。
2.JavaScript与传统的编译语言不同,它不是提前编译的,编译结果也不能在分布式系统中进行移植。
3.传统编译语言的流程,编译“三个步骤”
*分词/词法分析
将由字符组成的字符串分解成(对编程语言来说)有意义的代码块(被称为词法单元 token)。例如 var a = 2; 会被分解成var、a、=、2、;
*解析/语法分析
将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树(抽象语法树 Abstract Syntax Tree ,AST)。如var a = 2;的抽象语法树中可能有一个叫做VariableDeclaration的顶级节点,接下来是一个叫做Identifier(值为a)的子节点,以及一个叫做AssignmentExpression的子节点。AssignmentExpression节点有一个叫做NumericLiteral(值为2)的子节点
*代码生成
将AST转换为可执行代码的过程。将AST转换为一组机器指令,用来创建一个叫a的变量,并将一个值存储在a中。
比起只有三个步骤的语言的编译器,js引擎要复杂得多。
4.理解作用域,可以将这个过程模拟成几个任务之间的对话。
*引擎 从头到尾负责整个JavaScript程序的编译及执行过程
*编译器 引擎的好朋友之一,负责语法分析及代码生成等脏活累活
*作用域 引擎的另一个好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
5.变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没用声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它赋值。
第二步,引擎有两种查找方式。LHS跟RHS。顾名思义,L R代表左侧,右侧。
LHS(赋值操作的目标是谁), RHS(谁是赋值操作的源头)简单来说就是在赋值操作的左边 LHS 赋值操作的右边 RHS
例如 function foo(a) {
console.log(a);//2
}
foo(2);
该例子中a = 2,a是赋值操作的目标对a进行LHS,这里2是参数传递进来 隐式的分配,foo(...)是赋值操作的源头所有RHS,意味着“去找到foo的值,并把它给我”,还有一个console.log(..)本身也需要一个引用才能执行,也有对a的RHS引用。
例如function foo(a){
var b = a;
return a + b ;
}
var c = foo(2);
摘抄一下看别人写的好的微博的解析
以上代码中有3个LHS与4个RHS,分析如下:
第一,var c中的c需要被赋值,在赋值操作的左侧,所以对c进行LHS引用
第二,变量c需要被赋值,他的值是foo(2),那么foo(2)的值是多少呢,需要查找foo(2)的值,在赋值操作的右侧,所以对foo(2)进行RHS引用
第三,隐含赋值操作,将2传递给function foo(a){……}函数的参数a,a在赋值操作的左侧,对a进行LHS引用
第四,var b=a;中,b需要被赋值,处在赋值操作的左侧,所以b进行的LHS,b的值将从a来,那么右侧的a的值从何而来呢?这就需要对赋值操作右侧的a进行RHS。
第五,return a+b;中,需要找到a与b的值的来源,a与b都在赋值操作的右侧,才能得到a+b的值,所以对a与b都是进行RHS引用。
6.当一个块或者函数嵌套在另一个块或者函数中时,就发生了作用域嵌套。
LHS和RHS引用都会在当前作用域进行查找,如果没有找到,就会往上级作用域查找,直到全局作用域。到全局作用域后,无论如何查找过程都将停止。
7.为什么区分LHS和RHS是一件重要的事情
function foo(a){
console.log(a+b);
b = a;
c = {};
console.log(c.list.name);
}
foo(2);
第一次对b进行RHS查询时是无法找到该变量的。也就是说“未声明”的变量,在查询所有嵌套的作用域都找不到时,引擎抛出错误ReferenceError。

相比下,LHS查询,如果全局作用域也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量,并将其返回引擎,前提是非“严格模式”。
如果RHS查询到了变量,但是对变量进行的不合理操作。比如对一个非函数类型的值进行函数调用,或者引用null或undefined类型的值的中的属性,抛出TypeError,当然先删掉了上面的b的RHS错误再操作的,因为不删掉在执行到第一个查询的时候就已经出错不再执行到下一个了。

第二章
词法作用域
1.作用域共有两种主要的工作模型。第一种最为普遍,被大多数编程语言所采用,词法作用域。 另外一种叫做动态作用域。
2.欺骗词法。
js中的eval(..)函数可以接受一个字符串为参数,并将其中的内容视为好像在书写时就在那一样,引擎并不知道或者在意代码是否以动态形式插入,只会如往常一样进行查找。
function foo(str,a){
eval(str);//欺骗!
console.log( a, b);
}
var b = 2;
foo ( "var b = 3;" , 1); // 1,3
eval(..)通常被用来执行动态创建的代码。
注意:严格模式下,eval运行时有自己的词法作用域,意味着无法修改。
function foo (str){
"use strict"
eval( str );
console.log( a );
}
foo ( "var a = 2");

with简化对象属性赋值
function foo(obj){
with(obj){
a = 2;
}
}
var o1 = {
a: 3
};
var o2 = {
b: 3
};
foo(o1);
console.log(o1.a);
foo(o2);
console.log(o2.a);
console.log(o2.b);
console.log(a);//a 泄漏为全局变量

这里a会泄漏为全局变量的原因主要是,在非严格模式中,调用foo(o2) a = 2 时进行了LHS的查找,找不到,然后定义了一个a的全局变量。尝试将 foo(o2)删除后出现以下结果。

这两个机制的副作用是引擎无法在编译时对作用域查找进行优化;因为引擎只能谨慎地认为这样的优化是无效的。使用这其中任何一个机制都将导致代码运行变慢。所以最好不要使用它们。
你不知道的JavaScript 上卷 2/11的更多相关文章
- 你不知道的JavaScript上卷笔记
你不知道的JavaScript上卷笔记 前言 You don't know JavaScript是github上一个系列文章 初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目 ...
- 读《你不知道的JavaScript(上卷)》后感-作用域闭包(二)
github原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们, ...
- 你不知道的javaScript上卷(第一章 作用域是什么)
在写这篇博客时这本书我已经是看过一遍了,为了加深印象和深入学习于是打算做这系列的前端经典书籍导读博文,大家如果觉得这本书讲的好可以自己买来看看,我是比较喜欢看纸质版书的,因为这样才有读书的那种感觉. ...
- 读《你不知道的JavaScript(上卷)》后感-浅谈JavaScript作用域(一)
原文 一. 序言 最近我在读一本书:<你不知道的JavaScript>,这书分为上中卷,内容非常丰富,认真细读,能学到非常多JavaScript的知识点,希望广大的前端同胞们,也入手看看这 ...
- 《你不知道的 JavaScript 上卷》 学习笔记
第一部分: 作用域和闭包 一.作用域 1. 作用域:存储变量并且查找变量的规则 2. 源代码在执行之前(编译)会经历三个步骤: 分词/此法分析:将代码字符串分解成有意义的代码块(词法单元) 解析/语法 ...
- 【你不知道的javaScript 上卷 笔记3】javaScript中的声明提升表现
console.log( a ); var a = 2; 执行输出undefined a = 2; var a; console.log( a ); 执行输出2 说明:javaScript 运行时在编 ...
- JS闭包—你不知道的JavaScript上卷读书笔记(二)
关于闭包,初学者会被绕的晕头转向,在学习的路上也付出了很多精力来理解. 让我们一起来揭开闭包神秘的面纱. 闭包晦涩的定义 看过很多关于闭包的定义,很多讲的云里雾里,晦涩难懂.让不少人以为闭包是多么玄乎 ...
- JavaScript词法作用域—你不知道的JavaScript上卷读书笔记(一)
前段时间在每天往返的地铁上抽空将 <你不知道的JavaScript(上卷)>读了一遍,这本书很多部分写的很是精妙,对于接触前端时间不太久的人来说,就好像是叩开了JavaScript的另一扇 ...
- 《你不知道的JavaScript上卷》知识点笔记
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px "PingFang SC" } p.p2 { margin: 0.0px ...
随机推荐
- Linux虚拟网络:Docker网络知识之基础篇
我们在工作中应用了docker容器化技术,服务的部署.维护和扩展都方便了很多.然而,近期在私有化部署过程中,由于不同服务器环境的复杂多变,常常遇到网络方面的问题,现象为容器服务运行正常,但宿主机.容器 ...
- 07 . Kubernetes之Service
kubernetes有三种网络 1. Node Network 2. Pod Network 3. Cluster Network Service-网络代理模式 **userspce: 1.1- ** ...
- 虚拟机 - NAT模式下设置静态 IP 地址
背景 如果不给虚拟机设置静态 IP 地址的话,每次重启机器都会自动分配一个新的 IP 如果有多台虚拟机的话,也会动态获取 IP 动态IP的话,每次 设置静态 IP 的步骤 查看本机 IP 和网关 cm ...
- 自动化测试平台(Vue前端框架安装配置)
Vue简介: 通俗的来说Vue是前端框架,用来写html的框架,可轻量级也可不轻量级 Vue特性: 绑定性,响应性,实时性,组件性 安装软件以及控件: 控件库:element-ui node.js ( ...
- appium升级操作
在app自动化测试中经常会碰到,因为appium版本低而导致,appium客户端连接不到appium服务端等一系列错误~ 其实appium升级很简单的哦~ 打开cmd命令行终端,键入npm updat ...
- 【转】Web端测试点整理
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qq_24373725/article/d ...
- adb devices 不能连接设备 could not install *smartsocket* listener
cmd以管理员身份运行命令adb devices 或adb reverse tcp:8081 tcp:8081,无法连接设备,出现上图信息. 输入命令:adb kill-server 再输入:adb ...
- CCNA-Part5 - 传输层 ,TCP 为什么是三次握手?
传输层 传输层主要的作用就是建立端到端的连接.比如电脑的微信的通信,就需要跨越多个网络设备(交换机和录取)再和微信的服务器建立连接. 传输层需要具有以下的特点: 会话的多复用:如电脑上开启的多个应用, ...
- day18 作业
目录 1.编写课上讲解的有参装饰器准备明天默写 2.在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作 3.编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017- ...
- (1)为什么要使用webpack?
1.在网页中有哪些常见的静态资源? Js: .js .jsx .coffee .ts Css: .css .less .sass .scss Images: .jpg .png .gif .bmp . ...