每个编程语言都有作用域的概念,不管是C,C++,Java还是JavaScript,概莫例外!
作用域最主要的作用是访问空间的限制,也就是说,作用域内定义的变量或者函数,
只对该作用域或者其子子孙孙作用域可见,两个不是直系关系的作用域是无法直接访问其内部定义的变量和函数的
至于作用域是全局的,还是局部的,这个其实不是绝对的,因为任一作用域内定义的变量或函数对于其自身来说,都是局部的,但是对于直系子孙作用域来说,则是全局的
所以,从根作用域开始,到子孙作用域结束,这就像家谱一样组成了一个作用域链,每个作用域内变量或者函数的查找规则,就基于链式结构往上查的,直到根作用域为止,
那么查找具体是怎么做的呢?
对于强类型语言如C,C++和Java来说,变量的查找和确定在编译期就做了,也就是说,代码正式执行的时候,所有变量和函数的地址都已经确定(虚函数徐外)
这里重点讲JavaScript,JavaScript作为弱类型解释型语言,最大的优势就是灵活,那变量的查找当然也是动态查找最灵活
注意,下面是解释不是严格意义上ECMA标准协议的介绍,而是以一种比较通俗的方式来说明,这个要知晓。
我们都知道,JS的函数也是一个对象,一个用来描述函数代码的对象,详细可参考JavaScript原型和原型链新解
比如我们新建一个函数:
function test(){
let var1 = 'harish';
let var2 = 'heihei';
let var3 = var1 + var2;
}
test函数对象保存了函数执行所需的代码指令,以及代码执行过程中用到的变量名等等
有一点一定要了解,test对象保存的是静态代码以及相关资源数据,静态的!!!!
当test函数被执行的时候,其作用域内部的变量都是独立的,所以必须要创建一个运行时对象来动态的初始化并保存这些变量的数据,
这个对象被叫做函数的执行上下文(execution context):
text() -> execution-context.scope-chain: index 0 : var1,var2,var3,this
index 1: text所属parent函数的execution context
总结如下:
1:每一个函数调用,即作用域都会动态生成一个对应的execution context
2:这个函数的execution context对象中的scope chain中,index 0 保存为其作用域内定义的变量,以及this等,index 1保存为parent函数的execution context
3:函数内执行变量的查找,先从index 0对应的对象中查看是否存在,如果不存在,则跳到index1对应的parent函数的execution context中查找,依此类推,逐级向上,直到找到为止
作用域链就是这么回事,所以我们在开发中,要尽量避免某一变量在作用域链中过长的查找,这样肯定会影响性能
最后说下网上有朋友问的一个问题:
function test(){
let var1 = 'harish';
let var2 = 'heihei';
let var3 = var1 + var2;
}
test();
var tt = new test();
大体意思就是,直接调用test(), 跟new test()有什么区别?
如果按照java和c++的说法,就是一个是静态函数,一个是成员函数,一个execution context不包含this,一个execution context包含this,
就这么简单^_^