javascript中的作用域和上下文使用简要概述

javascript中的作用域(scope)和上下文(context)是这门语言的独到之处,这部分归功于他们带来的灵活性。每个函数有不同的变量上下文和作用域。这些概念是javascript中一些强大的设计模式的后盾。然而这也给开发人员带来很大困惑。下面全面揭示了javascript中的上下文和作用域的不同,以及各种设计模式如何使用他们。

上下文 vs 作用域

首先需要澄清的问题是上下文和作用域是不同的概念。多年来我注意到许多开发者经常将这两个术语混淆,错误的将一个描述为另一个。平心而论,这些术语变得非常混乱不堪。

每个函数调用都有与之相关的作用域和上下文。从根本上说,范围是基于函数(function-based)而上下文是基于对象(object-based)。换句话说,作用域是和每次函数调用时变量的访问有关,并且每次调用都是独立的。上下文总是关键字 this 的值,是调用当前可执行代码的对象的引用。

变量作用域

变量能够被定义在局部或者全局作用域,这导致运行时变量的访问来自不同的作用域。全局变量需被声明在函数体外,在整个运行过程中都存在,能在任何作用域中访问和修改。局部变量仅在函数体内定义,并且每次函数调用都有不同的作用域。这主题是仅在调用中的赋值,求值和对值的操作,不能访问作用域之外的值。

目前javascript不支持块级作用域,块级作用域指在if语句,switch语句,循环语句等语句块中定义变量,这意味着变量不能在语句块之外被访问。当前任何在语句块中定义的变量都能在语句块之外访问。然而,这种情况很快会得到改变,let 关键字已经正式添加到ES6规范。用它来代替var关键字可以将局部变量声明为块级作用域。

"this" 上下文

上下文通常是取决于一个函数如何被调用。当函数作为对象的方法被调用时,this 被设置为调用方法的对象:

复制代码 代码如下:


var object = {
foo: function(){
alert(this === object);
}
};

object.foo(); // true


同样的原理适用于当调用一个函数时通过new的操作符创建一个对象的实例。当以这种方式调用时,this 的值将被设置为新创建的实例:

复制代码 代码如下:


function foo(){
alert(this);
}

foo() // window
new foo() // foo


当调用一个未绑定函数,this 将被默认设置为 全局上下文(global context) 或window对象(如果在浏览器中)。然而如果函数在严格模式下被执行("use strict"),this的值将被默认设置为undefined。
执行上下文和作用域链

javascript是一个单线程语言,这意味着在浏览器中同时只能做一件事情。当javascript解释器初始执行代码,它首先默认竟如全局上下文。每次调用一个函数将会创建一个新的执行上下文。

这里经常发生混淆,这术语”执行上下文(execution context)“在这里的所要表达的意思是作用域,不是前面讨论的上下文。这是槽糕的命名,然而这术语ECMAScript规范所定义的,无奈的遵守吧。

每次新创建一个执行上下文,会被添加到作用域链的顶部,又是也成为执行或调用栈。浏览器总是运行在位于作用域链顶部当前执行上下文。一旦完成,它(当前执行上下文)将从栈顶被移除并且将控制权归还给之前的执行上下文。例如:

复制代码 代码如下:


function first(){
second();
function second(){
third();
function third(){
fourth();
function fourth(){
// do something
}
}
}
}
first();


运行前面的代码将会导致嵌套的函数被从上倒下执行直到 fourth 函数,此时作用域链从上到下为: fourth, third, second, first, global。fourth 函数能够访问全局变量和任何在first,second和third函数中定义的变量,就如同访问自己的变量一样。一旦fourth函数执行完成,fourth晕高兴上下文将被从作用域链顶端移除并且执行将返回到thrid函数。这一过程持续进行直到所有代码已完成执行。

不同执行上下文之间的变量命名冲突通过攀爬作用域链解决,从局部直到全局。这意味着具有相同名称的局部变量在作用域链中有更高的优先级。

简单的说,每次你试图访问函数执行上下文中的变量时,查找进程总是从自己的变量对象开始。如果在自己的变量对象中没发现要查找的变量,继续搜索作用域链。它将攀爬作用域链检查每一个执行上下文的变量对象去寻找和变量名称匹配的值。

闭包

当一个嵌套的函数在定义(作用域)的外面被访问,以至它可以在外部函数返回后被执行,此时一个闭包形成。它(闭包)维护(在内部函数中)对外部函数中局部变量,arguments和函数声明的访问。封装允许我们从外部作用域中隐藏和保护执行上下文,而暴露公共接口,通过接口进一步操作。一个简单的例子看起来如下:

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdwjxx.html