JavaScript作用域学习笔记
2017-11-26 16:42
253 查看
鸟哥:Javascript作用域原理
理解 JavaScript 作用域和作用域链
JavaScript中的函数运行在它们被定义的作用域里,而不是他们被执行的作用域里
函数对象和其他对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性,其中一个内部属性是[Scope],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
任何执行上下文时刻的作用域,都是由作用域链(scope chain)来实现的:
具体如下:
1.在一个函数被定义的时候,会将它定义时刻的scope chain链接到这个函数对象的[scope]属性。
因为add( )定义在全局环境,它的作用域链回填入一个全局对象,所以此时的[scope]指向全局活动对象,该全局对象包含了所有全局变量。
2.在一个函数对象被调用的时候,会创建一个活动对象(activation object),并创建arguments属性,然后对于每一个在这个函数中声明的局部变量和函数定义,都作为该活动对象的属性,然后将调用参数赋值给形参数,对于缺少的调用参数,赋值为undefined。然后将这个活动对象作为此时的scope chain最前端,并将这个函数对象的[scope]属性所指向的,定义该函数是的顶级活动对象加入到scope chain上。
新的作用域链如下:
4.有了上面的作用域链,在发生标识符解析的时候,就会逆向查询当前scope chain列表的每一个活动对象的属性,如果找到同名的就返回,找不到,就说明这个标识符没有被定义。
例:
当调用app的时候, scope chain是由: {window活动对象(全局)}->{app的活动对象} 组成.
在刚进入app函数体时, app的活动对象有一个arguments属性, 俩个值为undefined的属性: name和func. 和一个值为’eve’的属性para;
此时的scope chain如下:
当调用进入factory的函数体的时候, 此时的factory的scope chain为:
注意到, 此时的作用域链中, 并不包含app的活动对象.
在定义intro函数的时候, intro函数的[[scope]]为:
从factory函数返回以后,在app体内调用intor的时候, 发生了标识符解析, 而此时的sope chain是:
因为scope chain中,并不包含factory活动对象. 所以, name标识符解析的结果应该是factory活动对象中的name属性, 也就是’laruence’.
所以运行结果是:
从作用域链的结构可以看出,在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行期上下文作用域链的最末端,因此在标识符解析的时候,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。
改变作用域链
此外,通过with和try catch 语句可以改变运行期上下文的作用域链。
当代码运行到with语句时,运行期上下文的作用域链临时被改变了。一个新的可变对象被创建,它包含了参数指定的对象的所有属性。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在处于第二个作用域链对象中,因此访问代价更高了。
理解 JavaScript 作用域和作用域链
JavaScript中的函数运行在它们被定义的作用域里,而不是他们被执行的作用域里
函数对象和其他对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性,其中一个内部属性是[Scope],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
任何执行上下文时刻的作用域,都是由作用域链(scope chain)来实现的:
具体如下:
function add(num1,num2){ var sum=num1+num2; return sum; }
1.在一个函数被定义的时候,会将它定义时刻的scope chain链接到这个函数对象的[scope]属性。
因为add( )定义在全局环境,它的作用域链回填入一个全局对象,所以此时的[scope]指向全局活动对象,该全局对象包含了所有全局变量。
var total=add(5,10);
2.在一个函数对象被调用的时候,会创建一个活动对象(activation object),并创建arguments属性,然后对于每一个在这个函数中声明的局部变量和函数定义,都作为该活动对象的属性,然后将调用参数赋值给形参数,对于缺少的调用参数,赋值为undefined。然后将这个活动对象作为此时的scope chain最前端,并将这个函数对象的[scope]属性所指向的,定义该函数是的顶级活动对象加入到scope chain上。
新的作用域链如下:
4.有了上面的作用域链,在发生标识符解析的时候,就会逆向查询当前scope chain列表的每一个活动对象的属性,如果找到同名的就返回,找不到,就说明这个标识符没有被定义。
例:
function factory() { var name = 'laruence'; var intro = function(){ alert('I am ' + name); } return intro; } < 4000 span class="hljs-function">function app(para){ var name = para; var func = factory(); func(); } app('eve');//I am laruence
当调用app的时候, scope chain是由: {window活动对象(全局)}->{app的活动对象} 组成.
在刚进入app函数体时, app的活动对象有一个arguments属性, 俩个值为undefined的属性: name和func. 和一个值为’eve’的属性para;
此时的scope chain如下:
[[scope chain]] = [ { para : 'eve', name : undefined, func : undefined, arguments : [] }, { window call object } ]
当调用进入factory的函数体的时候, 此时的factory的scope chain为:
[[scope chain]] = [ { name : undefined, intor : undefined }, { window call object } ]
注意到, 此时的作用域链中, 并不包含app的活动对象.
在定义intro函数的时候, intro函数的[[scope]]为:
[[scope chain]] = [ { name : 'laruence', intor : undefined }, { window call object } ]
从factory函数返回以后,在app体内调用intor的时候, 发生了标识符解析, 而此时的sope chain是:
[[scope chain]] = [ { intro call object }, { name : 'laruence', intor : undefined }, { window call object } ]
因为scope chain中,并不包含factory活动对象. 所以, name标识符解析的结果应该是factory活动对象中的name属性, 也就是’laruence’.
所以运行结果是:
I am laruence
从作用域链的结构可以看出,在运行期上下文的作用域链中,标识符所在的位置越深,读写速度就会越慢。如上图所示,因为全局变量总是存在于运行期上下文作用域链的最末端,因此在标识符解析的时候,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。
改变作用域链
此外,通过with和try catch 语句可以改变运行期上下文的作用域链。
function initUI(){ with(document){ var bd=body, links=getElementsByTagName("a"), i=0, len=links.length; while(i < len){ update(links[i++]); } getElementById("btnInit").onclick=function(){ doSomething(); }; } }
当代码运行到with语句时,运行期上下文的作用域链临时被改变了。一个新的可变对象被创建,它包含了参数指定的对象的所有属性。这个对象将被推入作用域链的头部,这意味着函数的所有局部变量现在处于第二个作用域链对象中,因此访问代价更高了。
相关文章推荐
- JavaScript作用域学习笔记
- MySQL 数据类型(学习笔记)
- google_v8学习笔记:NO1 环境搭建以及代码获取
- 尚学堂Struts学习笔记1:Struts标签的配置和使用
- swift 学习笔记 - 数组,字典,元组
- MATLAB学习笔记 函数记录(二)
- Android 应用的安装与启动过程(学习笔记)
- 学习笔记_定K型和m推演型滤波器设计_LC滤波
- C++ STL 常用容器 学习笔记
- 小五入侵学习笔记
- MySQL入门很简单-学习笔记 - 索引页
- Qt Model/View 学习笔记 (一)
- 黑马程序员----交通灯学习笔记
- iBATIS.NET 学习笔记(九)
- Spring学习笔记1
- JavaWeb学习笔记-java基础-10-泛型
- java学习笔记之——文件输入流
- shell脚本攻略学习笔记(八)当个好管家
- springmvc学习笔记(8)-springmvc整合mybatis之service
- 学习笔记-dubbo监控和注册中心