第一部分——作用域和闭包

第一章

作用域是什么

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的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. 《你不知道的JavaScript上卷》知识点笔记

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px "PingFang SC" } p.p2 { margin: 0.0px ...

随机推荐

  1. 如何查看docker run启动参数命令

    通过runlike去查看一个容器的docker run启动参数 安装pip yum install -y python-pip 安装runlike pip install runlike 查看dock ...

  2. 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译

    源码学习第一步,Spring源码编译 之所以写这么一篇文章是因为群里的小伙伴在编译源码时碰到了问题,再加上笔者自身正准备做一个源码的注释版本,恰好也需要重新编译一份代码,至于为什么要将源码编译到本地就 ...

  3. 使用原生js来控制、修改CSS伪元素的方法总汇, 例如:before和:after

    在网页中,如果需要使用辅助性/装饰性的内容的时候,我们不应该直接写在HTML中,这样会影响真正的内容,这就需要使用伪元素了,这是由于css的纯粹语义化是没有意义的.在使用伪元素的时候,会发现js并不真 ...

  4. 如何使用SVG及其动画技术为你的 Web 前端开发带来一些新鲜的体验

    任何有开发经验的前端工程师都会考虑到不成体系的设备生态所带来的挑战.设备间不同的屏幕尺寸.分辨率和比例使得产品难以提供一致的体验,对于那些对产品有着像素级完美追求的人这种体验差异尤其显著! SVG(可 ...

  5. fastjson到底做错了什么?为什么会被频繁爆出漏洞?

    GitHub 15.8k Star 的Java工程师成神之路,不来了解一下吗! GitHub 15.8k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 15.8k Star ...

  6. 状压DP之愤怒的小鸟

    题目 传送们P2831 题目较长,不加以赘述 直接步入正题 首先是数学知识,我们可以先根据给出的任意两只猪构建相应的抛物线,同时再构建完之后应判断抛物线的合法性(比如a小于0啊,等等),公式推演就不在 ...

  7. windows下的包管理器scoop

    scoop(传送门) 安装 scoop是一个类似于linux下apt之类包管理器 安装scoop(Powershell 3+  and .NET Framework 4.5+) iex (new-ob ...

  8. MYSQL 之 JDBC(八):增删改查(六)ReflectionUtils

    这里在网上找了一份ReflectionUtils package com.litian.jdbc; /** * @author: Li Tian * @contact: litian_cup@163. ...

  9. MySQL 三万字精华总结 + 面试100 问,吊打面试官绰绰有余(收藏系列)

    写在之前:不建议那种上来就是各种面试题罗列,然后背书式的去记忆,对技术的提升帮助很小,对正经面试也没什么帮助,有点东西的面试官深挖下就懵逼了. 个人建议把面试题看作是费曼学习法中的回顾.简化的环节,准 ...

  10. 像写Flutter一样开发Android原生应用

    要问到Flutter和Android原生App,在开发是有何区别,编程方式是绕不开的话题.Flutter采用声明式编程,Android原生开发则采用命令式编程. 声明式编程 VS. 命令式编程 我们首 ...