来看几段特殊JavaScript的代码(写成这样会被打死)
-
12.toString 为什么会报错。
中间涉及到许多的词法分析,具体可以去看 winter 老师的重学前端、前端进阶训练营,再具体你可以直接去啃 ECMA-262去。
对于 12.toString 为什么会报错?
我们来看,我们写一个 NumberLiteral 的时候,有这些写法。
- 12
-
- Number(12)
- ….
我们看 ’12.’,还记不记得我们写小数的时候,零点几 我们可以写成 0.12341,也可以写成 .12341。其实在这里,是因为没有对 Number 原型的 prototype 方法的引用,我们写成下面这个样子也就 ok 了。 12. string(),记住中间有一个空格,还有一种方式是 12..toString()
-
var 声明
var 声明永远作用于脚本、模块和函数体这个级别,在预处理阶段,不关心赋值的部分,只管在当前作用域声明这个变量。
var a = 1; function foo() { console.log(a); // 1 } foo();
上面这段代码,函数 foo 成功读取到外部变量 a 的值
var a = 1; function foo() { console.log(a); //undefined var a = 2; } foo();
很奇怪把,这里是 undefined。这是因为老生常谈的 var 预处理问题。
这段代码声明了一个脚本级别的 a,又声明了 foo 函数体级别的 a,我们注意到,函数体的 var 出现在 console.log 语句之后。
但是在预处理过程执行之前,因为有了函数体级别的 a,就不会访问外层作用域中的变量 a 了,而函数体级的变量 a 此时还没有赋值,所以是 undefined。
var a = 1; function foo() { console.log(a); // undefined if(false) { var a = 2; } } foo();
这段代码比上一段代码在 var a = 2 之外多了一段 f,我们知道 if(false) 中的代码是永远不会执行的,但是预处理阶段根本不管这个,var 的作用能够穿透一切的structure,它只认脚本、模块还有函数体三种语法结构。所以这里结果跟前一段代码是一样的,我们得到了 undefined。
3.经典 var 面试题,防止 var 穿透的
for(var i = 0; i < 20; i++) { var div = document.createElement('div') div.innerHTML = i div.onclick = function() { console.log(i) } document.body.appendChild(div) }
这段代码为文档添加了 20 个 div 元素,并且绑定了点击事件,打印它们的序号。但是,实际上,上面的代码点击的时候 console 一直是 20。为什么呢,因为 for 循环的变量 i 会遇到 var 导致的变量提升,然后导致后面的代码console 的 i 会是 for 循环执行完了以后 var 变为 20的 i。
for(var i = 0;i < 20; i++) { void function(i) { var div = document.createElement('div') div.innerHTML = i div.onclick = function() { console.lgo(i) } document.body.appendChild(div) }(i) }
要改变这种情况,我们就使用 IIFE:
我们通过 IIFE 在循环内构造了作用域,每次循环都产生一个新的环境记录,这样,每个 div 都可以访问到环境中的 i 。