你不知道的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 ...
随机推荐
- 优化:在k8s上部署的gitlab
gitlab组件图 gitlab在k8s上占用资源 # kubectl top pods -n default | grep git* gitlab-gitaly-0 9m 444Mi gitlab- ...
- SQL注入原理及代码分析(一)
前言 我们都知道,学安全,懂SQL注入是重中之重,因为即使是现在SQL注入漏洞依然存在,只是相对于之前现在挖SQL注入变的困难了.而且知识点比较多,所以在这里总结一下.通过构造有缺陷的代码,来理解常见 ...
- 部署Redis Cluster 6.0 集群并开启密码认证 和 Redis-cluster-proxy负载
部署Redis Cluster集群并开启密码认证 如果只想简单的搭建Redis Cluster,不需要设置密码和公网访问,可以参考官方文档. 节点介绍 Cluster模式推荐最少有6个节点,本次实验搭 ...
- css3实现背景颜色渐变,文字颜色渐变,边框颜色渐变
css3的渐变可以使用2个或者多个指定的颜色之间显示平稳的过渡的效果.这篇文章主要介绍下css3实现背景颜色渐变,文字颜色渐变,边框颜色渐变的方法,以便大家学习参考! 1.css背景颜色渐变 代码: ...
- css如何设置不可点击?
通过设置元素的pointer-events属性设置为none,来实现元素不可点击.此方法是通过设置元素的鼠标事件失效来实现元素不可点击. css设置不可点击: css代码: .disable { po ...
- HotSpot的启动过程
HotSpot通常会通过java.exe或javaw.exe来调用/jdk/src/share/bin/main.c文件中的main()函数来启动虚拟机,使用Eclipse进行调试时,也会调用到这个入 ...
- KMP入门
First.先上一份最原始的无任何优化的代码(暴力): #include <iostream> #include <cstring> using namespace std; ...
- Flask——实现上传功能
1.实例 #!-*-coding=utf-8-*- # from flask import Flask # # app = Flask(__name__) # # # @app.route('/') ...
- Redis做为缓存的几个问题
缓存理流程: 前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果. 1.缓存雪崩 解决方案3:如果缓存数据库是分布 ...
- 005.Nginx配置下载站点
一 下载站点 1.1 下载站点配置 语法:autoindex on | off; 默认值:autoindex off; 配置段:http,server,location Nginx默认不允许列出整个目 ...