第四章:提升
-先有鸡(赋值)还是先有蛋(声明)
编译器再次袭来
-代码的任何部分被执行之前,所有的声明,变量和函数都会首先被处理
-变量和函数声明从代码流中出现的位置”移动”到代码的顶端(提升)
-先有蛋(声明)后有鸡(赋值)
-提升以作用域为单位
-函数表达式不会被提升
函数优先
-函数首先被提升然后才是变量
-普通块内部出现的函数声明一般会被提升至外围的作用域
-不可靠,避免在块中声明函数
复习
-var a = 2;JavaScript引擎将var a(编译期任务)和a = 2(执行时任务)分离处理
第五章:作用域闭包
启蒙
-在JavaScript中闭包无处不在,你只是必须认出它并接纳它
事实真相
-闭包定义
函数能够记住并访问它的词法作用域,即使当这个函数在它的词法作用域之外执行时
-子函数拥有一个词法作用域闭包覆盖着父函数的内部作用域
1.父函数被执行之后,子函数依然拥有对那个作用域的引用,这个引用称为闭包
2.子函数在它被编写的词法作用域之外被调用,闭包使之可以访问编写时被定义的词法作用域
-函数可以被作为值传递
-计时器、事件处理器、Ajax请求、跨窗口消息、web worker、或者任何其他的异步(或同步!)任务,当你传入一个 回调函数,你就在它周围悬挂了一些闭包!
循环+闭包
-超时的回调函数都将在循环完成之后立即运行
for (var i=1; i<=5; i++) {
setTimeout( functiontimer(){
console.log( i );
}, i*1000 );
}
-你会得到“6”被打印5次,一秒一个
for (var i=1; i<=5; i++) {
(function(j){
setTimeout( functiontimer(){
console.log( j );
}, j*1000 );
})( i );
}
-用IIFE函数为每次迭代时闭包一个新的作用域
-会打印1-5,一秒一个
重温块儿作用域
-let劫持一个块变成我们可以闭包的作用域
for (var i=1; i<=5; i++) {
let j = i; // 呀,给闭包的块儿作用域!
setTimeout( functiontimer(){
console.log( j );
}, j*1000 );
}
-let在循环头,这个变量将不只是为循环声明一次,而是为每次迭代声明一次,并且在每次后续的迭代中被上一次迭代末尾值初始化
for (let i=1; i<=5; i++) {
setTimeout( functiontimer(){
console.log( i );
}, i*1000 );
}
-块儿作用域和闭包携手工作,解决世界上所有问题
模块
-揭示模块:实现模块模式最常见的方法
-行使模块模式两个“必要条件”
1.必须有一个外部的外围函数,且至少被调用一次(每次创建一个新的模块实例)
2.外围函数必须至少返回一个内部函数,并且可以访问/修改这个私有状态
-在模块实例内部持有一个指向公用API对象的内部引用,可以从内部修改模块
现代的模块
-调用一个函数定义包装器,并将它的返回值作为这个模块的API保存下来
未来的模块
-ES6为模块的概念增加了头等的语法支持
-当通过模块系统加载时,ES6将一个文件视为一个独立的模块
-ES6模块没有“内联”格式,它们必须被定义在一个分离的文件中(每个模块一个)
-import导入一个模块的API的一个或多个成员
-export为当前模块的公用API导出一个标识符(变量/函数)
复习
-我们如何在函数即是值,而且可以被随意传递的词法作用域环境中编写代码
-感觉像JAVA中在一个类中定义方法,在其他地方导包后就可以调用方法
附录A:动态作用域
-不沿着嵌套的词法作用域链往上走一层,而是沿着调用栈向上走
-JavaScript实际上没有动态作用域,this机制有些像动态作用域
-关键差异
-词法作用域:编写时的,关心函数在何处被声明
-动态作用域(和this):运行时的,关心函数从何处被调用
附录B:填补块儿作用域
-ES6中let在前ES6可用catch块转换
-Traceur:Google维护的将ES6转译为前ES6代码
-隐含的和明确的块儿
使用更好和更明确的let语句形式,即使它还不是任何ES官方版本的一部分
-性能:使用块儿作用域,使用合理的工具
附录C:词法this
-var self = this免除了理解和正确使用this绑定的整个问题
-简单解释:摒弃了this绑定的所有一般规则,将它们的立即外围词法作用域作为this的值