深入浅析javascript中的作用域和上下文

上传人:bin****86 文档编号:59392353 上传时间:2018-11-07 格式:DOCX 页数:15 大小:22.28KB
返回 下载 相关 举报
深入浅析javascript中的作用域和上下文_第1页
第1页 / 共15页
深入浅析javascript中的作用域和上下文_第2页
第2页 / 共15页
深入浅析javascript中的作用域和上下文_第3页
第3页 / 共15页
深入浅析javascript中的作用域和上下文_第4页
第4页 / 共15页
深入浅析javascript中的作用域和上下文_第5页
第5页 / 共15页
点击查看更多>>
资源描述

《深入浅析javascript中的作用域和上下文》由会员分享,可在线阅读,更多相关《深入浅析javascript中的作用域和上下文(15页珍藏版)》请在金锄头文库上搜索。

1、我真正系统地接触和学习党的基本知识是在这次中级党校的培训班上。通过学习,了解了党的发展历程,对党的性质、宗旨、任务等基本知识有了进一步的了解深入浅析JavaScript中的作用域和上下文javascript中的作用域(scope)和上下文(context)是这门语言的独到之处,这部分归功于他们带来的灵活性。每个函数有不同的变量上下文和作用域。这些概念是javascript中一些强大的设计模式的后盾。然而这也给开发人员带来很大困惑。下面全面揭示了javascript中的上下文和作用域的不同,以及各种设计模式如何使用他们。上下文(Context)和作用域(Scope)首先需要知道的是,上下文和作用

2、域是两个完全不同的概念。多年来,我发现很多开发者会混淆这两个概念(包括我自己), 错误的将两个概念混淆了。平心而论,这些年来很多术语都被混乱的使用了。函数的每次调用都有与之紧密相关的作用域和上下文。从根本上来说,作用域是基于函数的,而上下文是基于对象的。 换句话说,作用域涉及到所被调用函数中的变量访问,并且不同的调用场景是不一样的。上下文始终是this关键字的值, 它是拥有(控制)当前所执行代码的对象的引用。变量作用域一个变量可以被定义在局部或者全局作用域中,这建立了在运行时(runtime)期间变量的访问性的不同作用域范围。 任何被定义的全局变量,意味着它需要在函数体的外部被声明,并且存活于

3、整个运行时(runtime),并且在任何作用域中都可以被访问到。 在ES6之前,局部变量只能存在于函数体中,并且函数的每次调用它们都拥有不同的作用域范围。 局部变量只能在其被调用期的作用域范围内被赋值、检索、操纵。需要注意,在ES6之前,JavaScript不支持块级作用域,这意味着在if语句、switch语句、for循环、while循环中无法支持块级作用域。 也就是说,ES6之前的JavaScript并不能构建类似于Java中的那样的块级作用域(变量不能在语句块外被访问到)。但是, 从ES6开始,你可以通过let关键字来定义变量,它修正了var关键字的缺点,能够让你像Java语言那样定义变量

4、,并且支持块级作用域。看两个例子:ES6之前,我们使用var关键字定义变量:function func() if (true) var tmp = 123;console.log(tmp); / 123之所以能够访问,是因为var关键字声明的变量有一个变量提升的过程。而在ES6场景,推荐使用let关键字定义变量:function func() if (true) let tmp = 123;console.log(tmp); / ReferenceError: tmp is not defined这种方式,能够避免很多错误。什么是this上下文上下文通常取决于函数是如何被调用的。当一个函数被作

5、为对象中的一个方法被调用的时候,this被设置为调用该方法的对象上:var obj = foo: function()alert(this = obj);obj.foo(); / true这个准则也适用于当调用函数时使用new操作符来创建对象的实例的情况下。在这种情况下,在函数的作用域内部this的值被设置为新创建的实例:function foo()alert(this);new foo() / foofoo() / window当调用一个为绑定函数时,this默认情况下是全局上下文,在浏览器中它指向window对象。需要注意的是,ES5引入了严格模式的概念, 如果启用了严格模式,此时上下文默

6、认为undefined。执行环境(execution context)JavaScript是一个单线程语言,意味着同一时间只能执行一个任务。当JavaScript解释器初始化执行代码时, 它首先默认进入全局执行环境(execution context),从此刻开始,函数的每次调用都会创建一个新的执行环境。这里会经常引起新手的困惑,这里提到了一个新的术语执行环境(execution context),它定义了变量或函数有权访问的其他数据,决定了它们各自的行为。 它更偏向于作用域的作用,而不是我们前面讨论的上下文(Context)。请务必仔细的区分执行环境和上下文这两个概念(注:英文容易造成混淆)

7、。 说实话,这是个非常糟糕的命名约定,但是它是ECMAScript规范制定的,你还是遵守吧。每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中(execution stack)。在函数执行完后,栈将其环境弹出, 把控制权返回给之前的执行环境。ECMAScript程序中的执行流正是由这个便利的机制控制着。执行环境可以分为创建和执行两个阶段。在创建阶段,解析器首先会创建一个变量对象(variable object,也称为活动对象 activation object), 它由定义在执行环境中的变量、函数声明、和参数组成。在这个阶段,作用域链会被初始化,this的值也

8、会被最终确定。 在执行阶段,代码被解释执行。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。 需要知道,我们无法手动访问这个对象,只有解析器才能访问它。作用域链(The Scope Chain)当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。 作用域链包含了在环境栈中的每个执行环境对应的变量对象。通过作用域链,可以决定变量的访问和标识符的解析。 注意,全局执行环境的变量对象始终都是作用域链的最后一个对象。我们来看一个例子:v

9、ar color = blue;function changeColor()var anotherColor = red;function swapColors()var tempColor = anotherColor;anotherColor = color;color = tempColor;/ 这里可以访问color, anotherColor, 和 tempColor/ 这里可以访问color 和 anotherColor,但是不能访问 tempColorswapColors();changeColor();/ 这里只能访问colorconsole.log(Color is now

10、+ color);上述代码一共包括三个执行环境:全局环境、changeColor()的局部环境、swapColors()的局部环境。 上述程序的作用域链如下图所示:从上图发现。内部环境可以通过作用域链访问所有的外部环境,但是外部环境不能访问内部环境中的任何变量和函数。 这些环境之间的联系是线性的、有次序的。对于标识符解析(变量名或函数名搜索)是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始, 然后逐级地向后(全局执行环境)回溯,直到找到标识符为止。闭包闭包是指有权访问另一函数作用域中的变量的函数。换句话说,在函数内定义一个嵌套的函数时,就构成了一个闭包, 它允许嵌套函

11、数访问外层函数的变量。通过返回嵌套函数,允许你维护对外部函数中局部变量、参数、和内函数声明的访问。 这种封装允许你在外部作用域中隐藏和保护执行环境,并且暴露公共接口,进而通过公共接口执行进一步的操作。可以看个简单的例子:function foo()var localVariable = private variable;return function bar()return localVariable;var getLocalVariable = foo();getLocalVariable() / private variable模块模式最流行的闭包类型之一,它允许你模拟公共的、私有的、和特

12、权成员:var Module = (function()var privateProperty = foo;function privateMethod(args)/ do somethingreturn publicProperty: ,publicMethod: function(args)/ do something,privilegedMethod: function(args)return privateMethod(args);)();模块类似于一个单例对象。由于在上面的代码中我们利用了(function() . )();的匿名函数形式,因此当编译器解析它的时候会立即执行。 在闭包

13、的执行上下文的外部唯一可以访问的对象是位于返回对象中的公共方法和属性。然而,因为执行上下文被保存的缘故, 所有的私有属性和方法将一直存在于应用的整个生命周期,这意味着我们只有通过公共方法才可以与它们交互。另一种类型的闭包被称为立即执行的函数表达式(IIFE)。其实它很简单,只不过是一个在全局环境中自执行的匿名函数而已:(function(window)var foo, bar;function private()/ do somethingwindow.Module = public: function()/ do something;)(this);对于保护全局命名空间免受变量污染而言,这种

14、表达式非常有用,它通过构建函数作用域的形式将变量与全局命名空间隔离, 并通过闭包的形式让它们存在于整个运行时(runtime)。在很多的应用和框架中,这种封装源代码的方式用处非常的流行, 通常都是通过暴露一个单一的全局接口的方式与外部进行交互。Call和Apply这两个方法内建在所有的函数中(它们是Function对象的原型方法),允许你在自定义上下文中执行函数。 不同点在于,call函数需要参数列表,而apply函数需要你提供一个参数数组。如下:var o = ;function f(a, b) return a + b;/ 将函数f作为o的方法,实际上就是重新设置函数f的上下文f.call(o, 1, 2); / 3f.apply(o, 1, 2); / 3两个结果是相同的,函数f在对象o的上下文中被调用,并提供了两个相同的参数1和2。在ES5中引入了Function.prototype.bind方法,用于控制函数的执行上下文,它会返回一个新的函数, 并且这个新函数会被永久的绑定到bind方法的第一个参数所指定的对象上,无论该函数被如何使用。 它通过闭包将函数引导到正确的上下文中。对于低版本浏览器,我们可以简单的对它进行实现如下(polyfill):if(!(bind in

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 办公文档 > 总结/报告

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号