《你不知道的 javascript(上卷)》

147 阅读6分钟

第一部分 作用域和闭包

1、 作用域是什么

作用域是一套规则,用于确定在何处以及如何查找变量(标识符)

1.1 编译原理

javascript 是一门编译语言,与传统的编译语言不同,它不是提前编译,编译结果也不能在分布式系统中进行移植。

词法分析 :

将字符串分解为代码块,代码块称为词法单元(token)。例如,考虑程序 var a = 2;。分解为 var 、a、=、2、;。

语法分析 :

将词法单元流(数组) 转换成一个由元素逐级嵌套所组成的代表了程序语法的结构的树(抽象语法树 abstract syntax tree AST)

代码生成 :

将AST转换为可执行代码的过程被称为代码生成。

1.2 理解作用域

LHS 赋值操作的目标 RHS 取到它的源值

1.3 作用域嵌套

LHS和RHS引用都会在当前楼层进行查找,如果没有找到,就会坐电梯上一层,如果还没有就继续向上,以此类推。一旦抵达顶层(全局作用域),可能找到了你所需的变量,也可能没有,但无论如何查找过程都会停止。

1.4 异常(ReferenceError)

不成功的RHS引用会导致抛出referenceError异常(console.log(a) 获取变量的值)。不成功的LHS引用会导致自动隐式地创建一个全局变量(非严格模式)(a=1 进行赋值)

2、 词法作用域

作用域共用两种主要的工作模式。第一种是词法作用域,第二种是动态作用域(javascript所采用的是词法作用域)

2.1 词法阶段

词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域写代码时将变量和块作用域写在哪里决定的,因此当词法分析器处理代码时会保持作用域不变。

查找(遮蔽效应)

作用域查找会在找到第一个匹配的标识符时停止。在多层的嵌套作用域中可以定义同名的标识符(作用域查找始终从运行时所处的最内部作用域开始,逐级向上,直到遇见第一个匹配的标识符为止)

函数作用域和块作用域

3.1 函数中的作用域

函数作用域:这个函数的全部变量都可以在这个函数里使用及复用(事实上在嵌套的作用域中也可以使用)

3.2 隐藏内部实现

我们的认知是先声明一个函数,然后里面添加代码。反过来实际上是把代码隐藏在函数声明中。

为什么“隐藏”变量和函数是一个有用的技术? (最小授权原则和最小暴露原则) 避免一些私有变量或函数变为全局变量

3.3 函数作用域

如果function是声明中的第一个词,就是函数声明,否则就是函数表达式。

函数声明和函数表达式之间最重要的区别是它们的名称标识符将会绑定在何处。

立即执行函数表达式==IIFE(immediately invoked function expression)

3.4 块作用域

for循环使用的变量污染整个函数作用域

let进行声明不会在块作用域中进行提升

垃圾回收机制

提升

结果是:2;
结果是:undefined;

引擎会在解释js代码之前首先对其进行编译。编译时会找到所有的声明去和作用域关联起来。

函数表达式不会提升(错误提示:TypeError)

foo表达式的变量标识符会被提升但是并不会赋值(所以是undefined),foo()调用undefined值是非法操作,所以抛出TypeError异常

函数优先提升(后面的函数声明会覆盖前面的)

作用域闭包

1.将函数类型的值传递到所在的词法作用域以外

2.只要使用了回调函数,实际上就是在使用闭包

3.闭包就是每一个的函数有一个的作用域(我的理解)

模块模式

1.必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会创建一个新的模块实例)。

2.封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态

动态作用域

动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调用。作用域链是基于调用栈的,而不是代码中的作用域嵌套

词法作用域是在写代码或者说定义是确定的,而动态作用域是在运行时候确定的。

词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用

第二部分 this和对象原型

默认绑定: 函数调用时无任何调用前缀的情景,默认绑定时this指向全局对象

隐式绑定: 调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含。

注意:对象属性引用链中只有最顶层或者说最后一层会影响调用位置(this的指向)

隐式丢失: 下面三点都将丢失this绑定(1.将隐式绑定变为默认绑定 2.赋值 3.回调函数)

显式绑定: call(...)和apply(...)第一个参数是一个对象,它们会把这个对象绑定到this,接着在调用函数时指定这个this.

new绑定:

1.创建(或者说构造)一个全新的对象

2.这个新对象会被执行[原型]连接

3.这个新对象会绑定到函数调用的this

4.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

优先级

new绑定>显示绑定>隐式绑定

第三章 对象

对象有两种形式定义:声明(文字)形式和构造形式

var myObj={key:value} 文字形式 | var myObj=new Object(); 构造形式

类型一共6种(string,number,boolean,null,undefined,object)简单基本类型本身并不是对象。(null 为 object)

js中二进制前三位都为0的话会被判断为object类型。null的二进制表示是全0.

.a语法通常被称为“属性访问” | ["a"]语法通常被称为“键访问”

属性名永远都是字符串,其他值(例如:true,3,{})作为属性名,那它也会被转为一个字符串

属性设置和屏蔽

所有的对象都是由Object继承而来,而Object对象却是一个函数。

每一个函数都有一个属性叫做prototype,它的属性值是一个对象,在这个对象中默认有一个constructor属性

每一个函数都有一个属性叫做prototype,它的属性值是一个对象。

每一个对象都有一个隐式原型proto,它引用了创建这个对象的函数的prototype。