ES6学习——新的语法:Temporal Dead Zone(TDZ)

应该说TDZ在JS中是一个比较新的概念,在规范里我也没有搜到对这个概念的具体定义,主要涉及let/const,函数参数默认值,subclass等的使用中。


(一)下面先看let/const中的TDZ问题,规范中这样写到:

13.3 let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.


看个通俗的代码示例在解释:

	'use strict';
	
	{ 	// enter new scope, TDZ starts
		tmp = 'abc'; // Uncaught ReferenceError: tmp is not defined
		console.log(tmp); // Uncaught ReferenceError: tmp is not defined
		let tmp; // TDZ ends, `tmp` = `undefined`
		console.log(tmp); // undefined
		tmp = 123;
		console.log(tmp); // 123
	}

规范中的意思就是,用let/const声明的变量,在声明之前访问时,会抛出ReferenceError。而用var声明的变量,声明之前访问它的时候,值会默认为undefined。

仔细看规范中的描述,在进入一个scope之后,let/const也会有一个hoist,这个过程和var一样,区别是var在hoist之后,被初始化为了undefined,但是let/const没有初始化,直到真正声明它的地方。


在看一个例子:

{ // enter new scope, TDZ starts
    const func = function () {
        console.log(myVar); // OK!
    };
    //这之后
    //访问myVar都会报ReferenceError
    //这之前
    let myVar = 3; // TDZ ends
    func(); 
}

上面这个例子说明了TDZ是一个动态的问题,就是真正访问这个变量时才会进行这种检查。


在看几个代码示例:

1:

let x = x;//Uncaught ReferenceError: x is not defined

2:

let a = f();
const b = 2;
function f() { return b; }//Uncaught ReferenceError: b is not defined

3:

{
console.log(typeof tmp); // Uncaught ReferenceError: tmp is not defined
let tmp;
}

对于第一个,第二个例子,请仔细对照规范去理解。对于第三个例子,想要检查一个let/const变量的类型,可以在非strict mode下用window.tmp去做检查,但并不推荐这样做,因为没有什么好的理由要这样去检查。

*以上全部代码在Chrome 47下通过测试


(二)下面看函数参数默认值中的TDZ问题

*以下全部代码在Kinoma Studio下通过测试,Chrome 47还不支持函数参数默认值

let b = 1;
(function(a = b, b) {//Error: get b: not initialized yet!
    trace(a + b);
}(undefined, 2));

(function(a = a) {}());//Error: get a: not initialized yet!

函数参数列表这里其实形成了一个作用域,这个作用域介于函数外层作用域和函数体作用域之间,我把上面的代码转换一下,就更容易理解了:

let b = 1;
(function({let a = b; let b}) {//把这里当成一个作用域
    trace(a+b);
}(undefined, 2));


然后在根据规范中对let的描述,就知道为什么a=b这里会报异常了,同样,a=a这里也会报错

对函数参数默认值更深入的理解,后面的章节中会具体讲解


(三)subclass中的TDZ问题

*以下全部代码在Chrome 47下通过测试

	'use strict';
	
	class A{}
	
	class B extends A{
		constructor(x){
			this.x = x;//Uncaught ReferenceError: this is not defined
		}
	}
	
	var b = new B(0);
	
	console.log(b.x);

上面这里的this是在父类里创建出来的,如果在使用this之前,没有调用super(),会认为this没有被定义。具体class的讲解请后面会有,这里就不详细解释了。



*文章参考:http://jsrocks.org/2015/01/temporal-dead-zone-tdz-demystified/


阅读更多
文章标签: javascript ES6 TDZ
个人分类: ES6
所属专栏: ES6
上一篇ES6学习——新的语法:const
下一篇ES6学习——新的语法:函数参数Spread
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭