【有书共读08】JS核心技术开发解密读书笔记02
JavaScript核心技术开发解密(第四章)读书笔记
一、创建过程
变量对象的创建,依次经历了以下几个过程:
1.在chrome浏览器中,变量对象会首先获得函数的参数变量及其值;在firefox浏览器中,是直接将参数对象arguments保存在变量对象中。
2.依次获取当前上下文中所有的函数声明,也就是使用function关键字生命的函数。在变量对象中会以函数名建立一个属性,属性值为指向该函数所在的内存地址引用。如果函数名的属性已经存在,那么该属性的值会被新的引用覆盖。
3.依次获取当前上下文中的变量声明,也就是使用var关键字声明的变量。每找到一个变量声明,就在变量对象中就以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性不会被修改。
二、实例分析
1、变量提升
当执行代码时分为创建阶段和执行阶段,创建阶段只是创建变量的名字,并未赋值;执行阶段变量才会有值。通过以下两个例子解释变量提升:
//例子一 a = 2; var a; console.log(a); // 2 //例子二 console.log(a); // undefined var a = 2;
关于这两个代码片段,正确的思路是:
包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。
所以,在第一个例子中,a的声明会被提升到代码最前端,当执行打印时,a已经被正确的声明并且赋值为2;在第二个例子中,当执行打印时,变量a只被声明创建,并没有值,所以此时是undefined。两个例子的代码执行顺序如下:
//例子一 var a; // 创建 a = 2; // 执行 console.log(a); // 2 //例子二 var a; // 编译 console.log(a); // undefined a = 2; // 执行
2、函数提升
在JS中,函数是一等公民,函数会被提升到代码执行的最前端,所以可以在函数前调用函数,以下是函数提升的例子:
foo();//此处不会报错,因为函数被提升了 function foo () { console.log(a); // undefined var a = 2; }
函数声明有两种方式,function声明和函数表达式声明。两者的区别是,function声明的函数可以被提升,但函数表达式声明的函数被提升的只是函数名称,并非整个函数,所以函数表达式不能在执行前调用,举个例子:
foo(); // TypeError var foo = function () { // ... }
正确的思考思路是:
只有声明本身会被提升,而赋值或其他运行逻辑会留在原地。
同时也要记住,即使是具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用,举个例子:
//例子 foo(); // TypeError bar(); // ReferenceError var foo = function bar () {//此处函数的名字仍然是foo // ... } //实际执行顺序 var foo; foo(); // TypeError bar(); // ReferenceError foo = function () { var bar = ...self... // ... }
3、函数优先
函数声明和变量声明都会被提升,但是一个值得注意的细节是函数会首先被提升,然后才是变量。
foo(); // 1 var foo; function foo () { console.log(1); } foo = function () { console.log(2); }
这段代码会输出1而不是2,原因是这段代码会被JS引擎理解为如下形式:
function foo () { console.log(1); } foo(); // 1 foo = function () { console.log(2); }
注意:var foo尽管出现在function foo () ...的声明之前,但它是重复的声明(因此会被忽略),因为函数声明会被提升到普通变量之前。
尽管重复的var声明会被忽略掉,但出现在后面的函数声明还是可以覆盖前面的,如下例子:
foo(); // 3 function foo () { console.log(1); } var foo = function () { console.log(2); } function foo () { console.log(3); }
附上书中一道例子,结合前面所讲的,留给各位作为思考题,知道答案的小伙伴请在在评论区留下你的答案:
test(); function test () { console.log(foo); console.log(bar); var foo = 'Hello'; console.log(foo); var bar = function () { return 'world'; } function foo () { return 'hello'; } }
三、全局上下文的变量对象
在全局作用域中,全局对象就是windows对象,而且全局上下文的变量对象不能变成活动对象(AO,JavaScript核心技术解密P24)。全局上下文会跟着上下文环境一直存在,只要浏览器不关闭,上下文环境可以直接访问全局上下文的属性。