作用域链、变量对象、闭包

变量对象:

摘自汤姆大叔的博客:http://www.cnblogs.com/TomXu/archive/2012/01/16/2309728.html

1.变量对象(variable object) 是与执行上下文相关的 数据作用域(scope of data) 。

它是与上下文关联的特殊对象,用于存储被定义在上下文中的 变量(variables) 、 函数声明(function declarations) 和 函数的形参 。

2.就像我们所说的, VO就是执行上下文的属性(property):

activeExecutionContext = {

VO: {

// 上下文数据(var, FD, function arguments)

}

};

3.在函数执行上下文中,VO是不能直接访问的,此时由活动对象(activation object,缩写为AO)扮演VO的角色。

4.活动对象是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。arguments属性的值是Arguments对象:

AO = {

arguments: <ArgO>

};

****5.执行上下文的代码被分成两个基本的阶段来处理:

  1. 进入执行上下文:这个阶段AO/VO已经拥有了属性,不过并不是所有的属性都有值,大部分属性的值还是系统默认的初始值undefined
  2. 执行代码:这个阶段AO/VO属性值被修改

6.通常,各类文章和JavaScript相关的书籍都声称:“不管是使用var关键字(在全局上下文)还是不使用var关键字(在任何地方),都可以声明一个变量”。请记住,这是错误的概念:

任何时候,变量只能通过使用var关键字才能声明。

7.填充VO的优先级顺序是: 函数的形参 -> 函数申明 -> 变量申明。

作用域链:

摘自汤姆大叔的博客:http://www.cnblogs.com/TomXu/archive/2012/01/18/2312463.html

1.作用域链正是内部上下文所有变量对象(包括父变量对象)的列表。此链用来变量查询。

var x = 10;

function foo() {

var y = 20;

function bar() {

alert(x + y);

}

return bar;

}

foo()(); // 30

上面的例子中,“bar”上下文的作用域链包括AO(bar)、AO(foo)和VO(global)。

2.函数上下文的作用域链在函数调用时创建的,包含活动对象和这个函数内部的[[scope]]属性。下面我们将更详细的讨论一个函数的[[scope]]属性。

在上下文中示意如下:

activeExecutionContext = {

VO: {...}, // or AO

this: thisValue,

Scope: [ // Scope chain

// 所有变量对象的列表

// for identifiers lookup

]

};

其scope定义如下:

Scope = AO + [[Scope]]

<上面代码的意思是:活动对象是作用域数组的第一个对象,即添加到作用域链的前端。>

3.[[scope]]特点:

?[[scope]]是所有父变量对象的层级链,处于当前函数上下文之上,在函数创建时存于其中。

?注意这重要的一点--[[scope]]在函数创建时被存储--静态(不变的),永远永远,直至函数销毁。即:函数可以永不调用,但[[scope]]属性已经写入,并存储在函数对象中。

?另外一个需要考虑的是--与作用域链对比,[[scope]]是函数的一个属性而不是上下文。

4.如果一个属性在对象中没有直接找到,查询将在原型链中继续。即常说的二维链查找。

活动对象没有原型,我们可以在下面的例子中看到:

function foo() {

var x = 20;

function bar() {

alert(x);

}

bar();

}

Object.prototype.x = 10;

foo(); // 20

三、闭包:

摘自汤姆大叔的博客:http://www.cnblogs.com/TomXu/archive/2012/01/31/2330252.html

1.闭包是代码块和创建该代码块的上下文中数据的结合。

闭包是一系列代码块(在ECMAScript中是函数),并且静态保存所有父级的作用域。

闭包就是能够读取上层上下文内部变量的函数。

2.在ECMAScript中,所有的函数都是闭包,因为它们都是在创建的时候就保存了上层上下文的作用域链(除开异常的情况) (不管这个函数后续是否会激活 —— [[Scope]]在函数创建的时候就有了)

3.在ECMAScript中,同一个父上下文中创建的闭包是共用一个[[Scope]]属性的。也就是说,某个闭包对其中[[Scope]]的变量做修改会影响到其他闭包对其变量的读取

这就是说:所有的内部函数都共享同一个父作用域

var data = [];

for (var k = 0; k < 3; k++) {

data[k] = function () {

alert(k);

};

}

data[0](); // 3, 而不是0

data[1](); // 3, 而不是1

data[2](); // 3, 而不是2

上述例子就证明了 —— 同一个上下文中创建的闭包是共用一个[[Scope]]属性的。因此上层上下文中的变量“k”是可以很容易就被改变的。

activeContext.Scope = [

... // 其它变量对象

{data: [...], k: 3} // 活动对象

];

data[0].[[Scope]] === Scope;

data[1].[[Scope]] === Scope;

data[2].[[Scope]] === Scope;

这样一来,在函数激活的时候,最终使用到的k就已经变成了3了。如下所示,创建一个闭包就可以解决这个问题了:

var data = [];

for (var k = 0; k < 3; k++) {

data[k] = (function _helper(x) {

return function () {

alert(x);

};

})(k); // 传入"k"值

}

// 现在结果是正确的了

data[0](); // 0

data[1](); // 1

data[2](); // 2

函数“_helper”创建出来之后,通过传入参数“k”激活。其返回值也是个函数,该函数保存在对应的数组元素中。这种技术产生了如下效果: 在函数激活时,每次“_helper”都会创建一个新的变量对象,其中含有参数“x”,“x”的值就是传递进来的“k”的值。这样一来,返回的函数的[[Scope]]就成了如下所示:

data[0].[[Scope]] === [

... // 其它变量对象

父级上下文中的活动对象AO: {data: [...], k: 3},

_helper上下文中的活动对象AO: {x: 0}

];

data[1].[[Scope]] === [

... // 其它变量对象

父级上下文中的活动对象AO: {data: [...], k: 3},

_helper上下文中的活动对象AO: {x: 1}

];

data[2].[[Scope]] === [

... // 其它变量对象

父级上下文中的活动对象AO: {data: [...], k: 3},

_helper上下文中的活动对象AO: {x: 2}

];

我们看到,这时函数的[[Scope]]属性就有了真正想要的值了,为了达到这样的目的,我们不得不在[[Scope]]中创建额外的变量对象。

原文地址:https://www.cnblogs.com/Tangttt/p/9568633.html

时间: 2024-08-28 22:38:11

作用域链、变量对象、闭包的相关文章

js中函数的创建和调用都发生了什么?执行环境,函数作用域链,变量对象

博客搬迁,给你带来的不便,敬请谅解! http://www.suanliutudousi.com/2017/11/26/js%E4%B8%AD%E5%87%BD%E6%95%B0%E7%9A%84%E5%88%9B%E5%BB%BA%E5%92%8C%E8%B0%83%E7%94%A8%E9%83%BD%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83%E5%87%B

JavaScript的作用域和变量对象

变量对象 先来说说什么是变量对象.变量对象中又存储了什么东西吧. JavaScript中的运行环境包含全局运行环境和函数运行环境这两种,每进入到一个运行环境都会创建一个变量对象,这个对象中记录了在当前运行环境中能够訪问到的变量,它们以变量对象的属性形式存在.也就是说这个变量对象成为"作用域"这个抽象概念的实体. 同一时候,变量对象中的属性记录是有一定先后顺序的.而且属性值究竟是实际的值还是undefined也是分阶段的(进入上下文(函数開始调用,但还未运行内部的详细代码).运行代码阶段

关于js作用域链,以及闭包中的坑

eg:链式作用域,想在外部读取blogName的值得方法 <script>var authorName="山边小溪";function doSomething(){    var blogName="梦想天空"; function innerSay(){        console.log(blogName+"1");    }       innerSay();  }doSomething() </script> 在上

作用域 作用域链 闭包 思想 JS/C++比较

首先,我说的比较是指JS中这种思想/实现方式与C++编译原理中思想/实现方式的比较 参考链接:(比较易懂的介绍,我主要写个人理解) 作用域链: http://www.cnblogs.com/dolphinX/p/3280876.html 闭包:http://kb.cnblogs.com/page/110782/ 个人理解: 作用域链: 在JS中,function也是一种object的实例. 作用域的概念必须已经知晓. 作用域链:用于标识符解析:确定数据的存储位置以及数据作用域(数据访问).(应该

闭包,作用域链,垃圾回收,内存泄露

关于闭包,我翻了几遍书,看了几遍视频,查了一些资料,可是还是迷迷糊糊的,干脆自己动手来个总结吧 !欢迎指正... (- o -)~zZ 1. 什么是闭包? 来看一些关于闭包的定义: 闭包是指有权访问另一个函数作用域中变量的函数 --<JS高级程序设计第三版> p178 函数对象可以通过作用域链相关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性称为 ‘闭包’ . --<JS权威指南> p183 内部函数可以访问定义它们的外部函数的参数和变量(除了this和argument

从作用域链谈闭包

闭包(closure)是Javascript语言的一个难点,也是它的特色.非常多高级应用都要依靠闭包实现. 神马是闭包 关于闭包的概念,是婆说婆有理. 因而,我就翻阅了红皮书(p178)上对于闭包的陈述: 闭包是指有权訪问另外一个函数作用域中的变量的函数 这概念有点绕,拆分一下.从概念上说,闭包有两个特点: 1.函数 2.能訪问另外一个函数作用域中的变量 在ES 6之前,Javascript仅仅有函数作用域的概念.没有块级作用域(但catch捕获的异常 仅仅能在catch块中訪问)的概念(IIF

标识符解析、作用域链、运行期上下文、原型链、闭包

本文讲到的是如何从数据访问层面上提高JS 代码的执行效率.总的来讲有以下几条原则: 函数中读写局部变量总是最快的,而全局变量的读取则是最慢的: 尽可能地少用with 语句,因为它会增加with 语句以外的数据的访问代价: 闭包尽管强大,但不可滥用,否则会影响到执行速度以及内存: 嵌套的对象成员会明显影响性能,尽量少用: 避免多次访问对象成员或函数中的全局变量,尽量将它们赋值给局部变量以缓存. 这么几句话看似简单,但要深刻理解其中的道理则需涉及到JS的 标识符解析.作用域链.运行期上下文(又称为执

JS闭包、作用域链、垃圾回收、内存泄露相关知识小结

补充: 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 闭包的三个特性: 1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变量不会被垃圾回收机制回收 闭包的定义及其优缺点: 闭包 是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量 闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露. 闭包是javascript语言的一大

JS详细图解作用域链与闭包

JS详细图解作用域链与闭包 攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你是初入前端的朋友,我没有办法直观的告诉你闭包在实际开发中的无处不在,但是我可以告诉你,前端面试,必问闭包.面试官们常常用对闭包的了解程度来判定面试者的基础水平,保守估计,10个前端面试者,至少5个都死在闭包上. 可是为什么,闭包如此重要,还是有那么多人没有搞清楚呢?是因为大家不愿意学习吗?还真不是,

在chrome开发者工具中观察函数调用栈、作用域链与闭包

在chrome开发者工具中观察函数调用栈.作用域链与闭包 在chrome的开发者工具中,通过断点调试,我们能够非常方便的一步一步的观察JavaScript的执行过程,直观感知函数调用栈,作用域链,变量对象,闭包,this等关键信息的变化.因此,断点调试对于快速定位代码错误,快速了解代码的执行过程有着非常重要的作用,这也是我们前端开发者必不可少的一个高级技能. 当然如果你对JavaScript的这些基础概念[执行上下文,变量对象,闭包,this等]了解还不够的话,想要透彻掌握断点调试可能会有一些困