很高兴,看了这本书,我搞清楚了一件事,就是javascript这门语言写出的代码,在浏览器运行之前,到底需不需要编译。。,答案就是:需要,只不过和C++,java不同的是,javascript的编译和运行,都是由浏览器一个家伙完成,真是厉害厉害,此处发出赞叹!流程大概是这样的:
浏览器引擎拿到一个代码段--->词法分析--->语法分析--->代码生成--->代码优化--->执行
所有工作都只有非常少的时间去完成,所以真的没什么时间做很牛X的优化,所以其实对开发人员来说,代码写的好坏对性能影响会更大。好了,到现在为止,有的同学可能控制不了,“这个作者怎么这么多废话呢,能不能赶紧讲正题”,好的好的,同学,请把板砖放下,马上开始。
理解作用域,首先要明白一件事,就是我们定义的变量,代码块的作用域实在什么时候确定下来的? 这个时候我们由拉出来一个概念,“词法作用域”顾名思义,这个东西就是代码引擎在做词法分析的时候,根据你所写的代码的位置,来决定的。再通俗的说,就是你写的变量,代码块的位置,就决定了他的作用域,而且在大部分情况下,引擎的词法分析器会保持这个作用域是不变。---当然,万事不是绝对,有些时候可以采取一些特殊手段,我们称之为作用域欺骗(最好别用),后面我们再说他。
请看以下代码:
function hello(name){
var helloWord="hello " + name + " nice to meet you";
function say(praiseWord){
console.log(name,helloWord,praiseWord)
}
say("you are so beautiful");
}
hello("hanMeiMei");上面这段代码有三个逐级嵌套的作用域,从大到小排列:
1,全局作用域,只包含一个标识符:hello
2,hello函数创建的作用域,有三个标识符: helloWord,name, say
3,say函数创建的作用域,只有一个标识符:praiseWord
当引擎做词法分析的时候,遇到 console.log("name,helloWord,praiseWord"),会对该函数所使用到的标识符做作用域以及引用位置的预定义,以便在执行的时候能够更快速的找到它们,并且是由内部最小作用域向外进行查找,在 最内部作用域找不到 标识符 helloWord的时候,就会在外一层作用域进行查找,如果还是找不到,就继续向外层查找,直到找到第一个匹配的标识符为止。这样有一个问题,当多层作用域定义了相同名称的标识符的时候,引擎只会使用第一个匹配到的标识符的位置,也产生了“屏蔽效应”。内层作用域定义的标识符会将外层的同名标识符屏蔽。
也就是说,这样最常见的问题就是,你所定义的全局变量,会被内部作用域的同名变量屏蔽掉,产生莫名其妙的bug,头痛,这也是新手经常遇到的问题。。。,最简单的解决办法就是,使用全局变量的时候使用window.xxx,因为全局变量都会默认在window对象上附加的一个属性,使用window对象调用,就可以防止屏蔽效应的产生。函数的词法作用域也同样只取决于它声明时所处的位置,和调用无关。
我们前面说了,有些手段可以做到词法作用域欺骗,当然,这个是非常不鼓励去做的,有很多原因,首先我们看一下有哪些手段可以做到。
1,eval函数,这个函数可以接受一个字符串,可以将字符串作为代码运行,所以可以用代码生成代码。。。。
2,with关键字,这个比较冷僻的关键字,可以对一个对象的多个属性进行赋值。
有些同学可能会说了,eval我觉得很好用啊,而且能实现很多炫酷的功能,为什么不让用呢,(在严格模式下eval被禁止使用,在各个大厂的代码规范中也明确禁止使用),这是因为eval除了会产生安全问题以外(这个暂不分析。。),还会严重影响程序的性能,在前面我们说过,代码段在执行之前,引擎会进行优化,预先分析,确定所有变量和函数的的定义位置,为调用的时候提供快速查找,如果一旦出现了eval函数,因为引擎无法确定函数接受的参数是什么样子,所以会采取悲观措施,也就是说,认为对该代码段所做的优化都是无意义的,最坏的结果就是,对该代码段不做任何优化。。。,so,结果可想而知。
先扯到这里,下篇继续。