`
iwebcode
  • 浏览: 2007900 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

{{JS}}函数作用域和闭包

 
阅读更多
1.词法作用域
Javascript中的函数是通过词法来划分作用域的,而不是运态地划分作用域的,即“在Javascript中,函数运行在定义它们的作用域,而不是运行在执行它们的作用域中”。这句话可理解为:
当使用function定义一个函数时,当前作用域被保存起来,并且成为函数内部状态的一部分。在最顶级,使用域链仅由全局对象组成,而并不和词法作用域相关。然而当定义一个嵌套的函数时,作用域链就包括外围的包含函数。这意味着嵌套的函数可以访问包含函数的所有参数和局部变量。

注:尽管当一个函数定义时,作用域链便固定了,但作用域链中定义的属性还没有固定。作用域链是“活的”,并且函数在被调用时可以访问当前的绑定。

2.调用对象
当JS解释器调用一个函数时,它首先将作用域设置为定义函数的时候起作用的那个作用域链接着它在作用域链的前面添加一个新的对象,称为调用对象(call object),调用对象用一个名为arguments的属性来初始化,这个属性引用了函数的Arguments对象。函数的命名参数添加到调用对象的后面。而用var语句声明的任何局部变量也都定义在这个对象中。既然这个调用对象位于作用域链的前端,局部变量,函数参数及Arguments对象都在函数内的作用域中。当然,这也意味着它们可以隐藏作用域链更上层的任何同名属性。

注:和arguments不同,this是一个关键字,而不是调用对象的一个属性。

3.作为名字空间的调用对象
有时定义一个只是创建调用对象的函数,这个调用对象充当一个临时的名字空间,可以在该名字空间中定义变量并创建属性,而不会破坏全局的名字空间。如下:
(function() {
// todo :
})();

4.作为闭包的嵌入函数
Javascript允许嵌入的函数,允许函数用作数据,并且使用词法作用域,这些因素相互作用可以产生惊人的效果。比如考虑一个定义在函数f中的函数g。当f被调用时,作用域链包含了对f的这一调用的调用对象,后边是全局对象。g定义在f中,因此这个作用域链保存为g的定义的一部分。当g被调用时,作用域链包含3个对象:它自己的调用对象,f的调用对象及全局对象。

1)当嵌入的函数在它们定义的同一词法作用域中调用时,它们是很好理解的。如下
var a = "global";
function f() {
var a = "local";
function g() { console.log(a); }
g();
}
f();// local

2)考虑如下代码,其中包含了一个函数,它返回一个嵌套的函数。如下代码每次调用makefunc函数时它都返回一个函数。返回的函数的代码都一样。但它所创建的作用域略有不同,因为外围函数的参数值在每次调用时都不同,即外围函数的每次调用的作用链上有一个不同的调用对象。
function makefunc(x) {
return function() { return x; }
}

var a = [makefunc(0), makefunc(1), makefunc(2)];
console.log(a[0]()); // 0
console.log(a[1]()); // 1
console.log(a[2]()); // 2
这段代码的结果正是可以从严格应用词法作用域规则所期待的:函数在它所定义的作用域中执行。然而这些结果令人吃惊的原因是:当定义了局部作用域的函数退出时期待局部作用域能够终止并退出。当一个函数被调用时,就为它创建一个调用对象并放置到作用域链中。当函数退出时,调用对象也从作用域链中移除。当没有涉及嵌套的函数时,作用域链是对调用对象的惟一的引用。当对象从链中移除了,也就没有对它的引用了,最终通过对它的垃圾收集而完结。

但是,嵌套函数改变了这一情景。如果创建一个嵌套函数,这个函数的定义引用了调用对象,因为调用对象在这个函数所定义的作用域链的顶端。可是当嵌套函数只在外围函数的内部调用时,那么对嵌套函数的惟一引用在调用对象中。当外围函数返回时,嵌套函数引用了调用对象,并且调用对象引用了嵌套函数,但是没有其它东西引用它们二者,因此对这两个对象都可以进行垃圾收集了。

如果把嵌套函数的引用保存到一个全局作用域(另一个作用域)中,情况又不同了。使用嵌套的函数作为外围函数的返回值,或者把嵌套的函数存储为某个其它对象的属性。此时,有一个对嵌套函数的外部引用,并且嵌套函数将它的引用保留给外围函数的调用对象。结果是,外围函数的一次特定调用的调用对象仍然存在,函数的参数和局部变量的名字和值在这个对象中得以维持。Javascript代码不会以任何方式直接访问调用对象,但它所定义的属性是对嵌入函数任何调用的作用域链的一部分。(注:若一个外围函数存储了两个嵌套函数的全局引用,这两个嵌套函数共享同一个调用对象,并且,一个函数的一次引用所做出的改变对于另一个函数的调用来说是可见的。)

Javascript函数是将要执行的代码以及执行这些代码的作用域构成的一个综合体,这种代码和作用域的综合体称为闭包。所有的Javascript函数都是闭包。而当一个嵌套函数被导出到它所定义的作用域时,这种闭包才是最有趣的。所以当一个嵌套函数以这种方式使用时,我们明确地称之为闭包。

如下闭包的示例:
var uniqueId = (function() {
var id = 0;
return function() { return id++; };
})();

//模拟私有属性
function makeProperty(o, name, predicate) {
var value;
o["get" + name] = function() { return value; };
o["set" + name] = function(v) {
if(predicate && !predicate(v)) {
throw new Error("set" + name + ": invalid value " + v);
} else {
value = v;
}
}
}
























































分享到:
评论

相关推荐

    【JavaScript源代码】JS难点同步异步和作用域与闭包及原型和原型链详解.docx

     目录 JS三座大山同步异步同步异步区别作用域、闭包函数作用域链块作用域闭包闭包解决用var导致下标错误的问题投票机闭包两个面试题原型、原型链原型对象原型链完整原型链图 JS三座大山 同步异步同步异步区别...

    JavaScript作用域、闭包、对象与原型链概念及用法实例总结

    嵌套作用域变量搜索规则:当在函数中引用一个变量时,JS会搜索当前函数作用域,如果没有找到则搜索其上层作用域,一直到全局作用域。 var value = 'global'; var f1 = function(){ console.log

    JS匿名函数、闭包

    在后台执行环境中,闭包的作用域链包含着它自己的作用域、包含函数的作用域和全局作用域; 通常,函数的作用域及其所有变量都会在函数执行结束后被销毁; 但是,当函数返回了一个闭包时,这个函数的作用域将会...

    Web前端面试题目JavaScript(作用域,原型。原型链,闭包,封装函数).txt

    前端面试题,包含JavaScript的闭包,作用域,原型,原型链,上下文环境以及DOM,BOM封装函数深度克隆,以及一些常见的·JS问题,试题简单但是容易混淆,作为前端工程师必考题

    javascript作用域和闭包使用详解

    作用域JavaScript 中有两种作用域:函数作用域和全局作用域。 在一个函数中声明的变量以及该函数的参数享有同一个作用域,即函数作用域。一个简单的函数作用域的例子: 代码如下:function foo() { var bar = 1; {...

    浅谈JavaScript作用域和闭包

    作用域和闭包在JavaScript里非常重要。但是在我最初学习JavaScript的时候,却很难理解。这篇文章会用一些例子帮你理解它们。 我们先从作用域开始。 作用域 JavaScript的作用域限定了你可以访问哪些变量。有两种作用...

    深入了解JS之作用域和闭包

    作用域和闭包 ECMAScript5: JS 的代码没有代码块;使用函数运行的机制进行创建闭包;闭包就是作用域的意思; ES5中,JS中只有函数才可以创建能操作的作用域; JavaScript中的内存也分为栈内存和堆内存。一般来说,...

    JavaScript闭包函数

    闭包是ECMAScript (JavaScript)最强大的特性之一,但用好闭包的前提是必须理解闭包。闭包的创建相对容易,人们甚至会在...而闭包工作机制的实现很大程度上有赖于标识符(或者说对象属性)解析过程中作用域的角色。

    javascript 闭包、匿名函数、作用域链

    NULL 博文链接:https://xieyaxiong.iteye.com/blog/1558277

    JS页面获取 session 值,作用域和闭包学习笔记

    主要介绍了JS页面获取 session 值,作用域和闭包,结合具体实例形式分析了javascript与jsp交互获取session值、函数作用域及闭包相关操作技巧,需要的朋友可以参考下

    深入Javascript函数、递归与闭包(执行环境、变量对象与作用域链)使用详解

    本篇文章对Javascript中函数、递归与闭包(执行环境、变量对象与作用域链)的使用进行了详细的分析介绍。需要的朋友参考下

    javascript中的作用域和闭包详解

    JavaScript变量实际上只有两种作用域,全局变量和函数的内部变量。在函数内部任何一个地方定义的变量(var scope)其作用域都是整个函数体。 全局变量:指的是window对象下的对象属性。 作用域划分:基于上下文,以...

    深入理解javascript原型和闭包

    深入理解javascript原型和闭包(01)——一切都是对象 深入理解javascript原型和闭包(02)——函数和对象的关系

    JavaScript函数式编程

    全书共9章,分别介绍了JavaScript函数式编程、一等函数与Applicative编程、变量的作用域和闭包、高阶函数、由函数构建函数、递归、纯度和不变性以及更改政策、基于流的编程、类编程。除此之外,附录中还介绍了更多...

    深入理解javascript作用域和闭包

    作用域是一个变量和函数的作用范围,javascript中函数内声明的所有变量在函数体内始终是可见的,在javascript中有全局作用域和局部作用域,但是没有块级作用域,局部变量的优先级高于全局变量,通过几个示例来了解下...

    antgod#blog#3.编写函数式3-作用域与闭包1

    动态作用域在任何JS核心引擎中,都有一张全局表来存储全局变量// 动态作用域,任何JS核心引擎中,有个全局查找表const stackBinder = make

    javascript从作用域链谈闭包

    在ES 6之前,Javascript只有函数作用域的概念,没有块级作用域(但catch捕获的异常 只能在catch块中访问)的概念(IIFE可以创建局部作用域)。每个函数作用域都是封闭的,即外部是访问不到函数作用域中的变量。 ...

    JavaScript使用闭包模仿块级作用域操作示例

    正如闭包的定义一样:“闭包指的是有权访问另一个函数作用域中的变量的函数”, 闭包最大的意义就在于闭包可以对另一个函数作用域的变量进行访问,由此,闭包可以延伸出一系列的用法。 模仿块级作用域 JavaScript...

    js代码-闭包原理实例 ES5 只要函数作用域,无块级作用域

    js代码-闭包原理实例 ES5 只要函数作用域,无块级作用域

Global site tag (gtag.js) - Google Analytics