边读边总结--理解javascript作用域(一)

139 阅读5分钟
       要理解javascript的作用域概念,首先要明确一点,javascript作为一门前端语言,在很长一段时间都是被误解的,以前在学校的时候,听到老师说,javascript是一种脚本语言,是不需要编译的,可以直接由浏览器拿来直接运行,当时作为一个计算机小白,同时在学习C语言和javascript(其实是网页制作课程。。),感觉javascript好神奇,写了就可以直接运行,不需要一系列的编译过程。在学习的时候,又感觉这门语言非常的不严谨,一旦写的不够规范,或者说组织结构不够清晰有调理,经常会出现变量冲突,几个代码文件的变量相互覆盖的情况,十分令人抓狂。从毕业工作到现在,很多年过去了,从一名后端开发工程师,转入到了前端领域,匆忙之间做了两年前端开发。。,感觉代码可以写,但是还是有写东西理解的不够清晰,不够系统,幸好最近看了一本书:《你不知道的javascript》,感觉写的清晰易懂,获益良多,强烈推荐。在自己学习的同时,也来分享和总结一下自己的收获,大神们轻点拍砖,疼。

      

       很高兴,看了这本书,我搞清楚了一件事,就是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,结果可想而知。

先扯到这里,下篇继续。