《你不知道的JS》上卷
1.编译
- 分词/词法分析:将字符组成的字符串分解成有意义的代码块
- 解析/语法分析:将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的抽象语法树(AST)
- 代码生成:将AST转换成可执行代码
上面是传统编译语言的流程,JS的编译过程不是发生在构建之前,而是发生在代码执行前
2.LHS和RHS查找
LHS和RHS是引擎对变量的查找方式,L和R代表的是一个赋值操作的左侧和右侧。可以理解为赋值和取值。
console.log(a)
对a是引用是RHS饮用,这里a并没有赋予任何值
a = 2
对a的引用是LHS引用
为什么要区分LHS和RHS呢?当你未声明该变量时,进行RHS查询会抛出ReferenceError异常,而LHS查询会在全局作用域中创建一个该名称变量,但如果在严格模式下,禁止自动或隐式创建全局变量,也会抛出ReferenceError异常。如果RHS找到一个变量,但是对其进行不合理操作,会抛出TypeError异常。
RefersnceError同作用域判别失败有关,而TypeError则代表作用域判别成功,但是对结果的操作是非法的
3.匿名和具名函数
匿名函数的几个缺点
- 调试困难:匿名函数在栈追踪中不会显示出有意义的函数名
- 引用自身时只能使用已过期的arguments.callee引用:比如在递归中和事件触发后事件监听器需要解绑自身
- 可读性、可理解性下降:一个描述性名称可以让代码不言自明
4. 块作用域
将变量绑定在所在块作用域({…})ES6用let,之前用try/catch的catch块。
5. 变量提升
变量的声明会提到作用域的最上方,并且函数声明优先。
6. 闭包
当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这是就产生了闭包
7. 箭头函数
箭头函数在涉及绑定时的行为和普通函数的行为完全不一致。她放弃了所有普通this绑定的规则,取而代之的是用当前的词法作用域覆盖了this的值
相当于在函数外声明了that = this
,然后在函数内使用that来绑定
也可以在函数后面加上bind(this)
,也和箭头函数等价。但可以避免使用新特性。
8.this
this既不指向函数本身也不指向函数的词法作用域,this实际上是在函数被调用时发生的绑定,完全取决于函数的调用位置。是在运行时进行绑定的,并不是在编写时进行绑定的。
this的绑定规则
- 默认绑定(函数体处于严格模式下无效)
- 隐式绑定(被某个对象拥有或包含并且直接调用)
- 显式绑定
- call(obj, arg1, arg2…)
- apply(obj, [arg1, arg2…])
- bind(obj),返回一个硬编码的新函数
- new绑定:在js中,构造函数只是使用new操作符时被调用的函数,就是个普通函数。只是会自动执行下面操作
- 创建(或者说构造)一个全新的对象
- 这个新对象会被执行prototype连接
- 这个新对象会绑定到函数调用的this
- 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
this绑定优先级:new > 显式绑定 > 隐式绑定 > 默认绑定。
bind(null, arg1),bind可以传入null对参数进行柯里化(预先设置一些参数)
Object.create(null)和”{}”很像,但是不会创建Object.prototype这个委托,所以它比{}“更空”
9.对象
in操作符会检查属性名(不是属性的值)是否在其对象及其原型链上,而hasOwnProperty()只会检查属性是否在对象中。hasOwnProperty()也是用原型上的方法实现的,所以对象没有原型链的话,函数就会失败。
for…in…或者Object.keys(obj)循环用在对象上遍历属性名,如果属性名的可枚举属性enumerable设置成fasle,就遍历不到了。若要全部遍历出来,用Object.getOwnPropertyNames(obj)
for…of…循环用在数组上属性值
10.类
多态:父类的通用行为可以被子类的特殊行为重写
总而言之,js中尽量不要模拟类的实现。得不偿失
11.原型
a instanceof b
instanceof的判断依据:在a的整条[[Prototype]]链中是否有Foo.prototype指向的对象
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 (CC BY-NC-ND 4.0) 进行许可。