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

第一章

作用域是什么

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. Python之浅谈继承

    目录 继承 继承介绍 如何使用继承 新式类:只要继承了object类,就是新式类,再python3中,默认继承object类 经典类:没有继承object的类,就是经典类 利用继承减少代码冗余,菱形问 ...

  2. django 类视图的使用

    使用django框架也有挺长时间了,但是一直都没有用过django的类视图,因为之前跟着网上教程学习时,觉得类视图是进阶的知识,可能目前还达不到吧 但今天在做项目的时候用到了,感觉真的太方便了吧,而且 ...

  3. Linux 操作系统!开篇!!!

    此篇文章主要会带你介绍 Linux 操作系统,包括 Linux 本身.Linux 如何使用.以及系统调用和 Linux 是如何工作的. Linux 简介 UNIX 是一个交互式系统,用于同时处理多进程 ...

  4. 从Linux源码看Socket(TCP)Client端的Connect

    从Linux源码看Socket(TCP)Client端的Connect 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的 ...

  5. List集合-02.LinkedList

    2.LinkedList 2.1 UML继承关系图 2.2 底层存储节点 通过内部类Node存储,可以看出是双向的链表结构 private static class Node<E> { E ...

  6. cf1216E2 Numerical Sequence (hard version)(思维)

    cf1216E2 Numerical Sequence (hard version) 题目大意 一个无限长的数字序列,其组成为\(1 1 2 1 2 3 1.......1 2 ... n...\), ...

  7. 文件读取一些payload

    Windows: C:boot.ini //查看系统版本 C:WindowsSystem32inetsrvMetaBase.xml //IIS配置文件 C:Windowsrepairsam //存储系 ...

  8. Socket模拟Web服务器

    效果如下: 源码下载地址:https://github.com/doyoulaikeme/DotNetSample/tree/master/DotNetSample3/SocketWebServer

  9. nginx中的root和alias辨析

    root介绍 Syntax: root path; Default: root html; Context: http, server, location, if in location Sets t ...

  10. JVM 专题四:类加载子系统(二)双亲委派机制

    2. 双亲委派机制 2.1 双亲委派机制工作原理 2.1.1 原理 Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存,生成class对象 ...