关于 JavaScript 作用域您需要知道的一切






4.50/5 (4投票s)
关于 JavaScript 作用域的示例和解释。
引言
如今,在线应用程序越来越多,正如我们所知,JavaScript 对于任何 Web 应用程序都至关重要,无论是简单的还是富客户端应用程序(RIA)。理解 JavaScript 变得前所未有的重要,而 JavaScript 作用域是编写面向主要从事 Web 应用程序的软件专业人员的“防弹”代码的关键。
在本文中,我将详细介绍您应该了解的 JavaScript 作用域知识。我们将针对每个主题提供示例。
- 范围
- 全局作用域
- 局部作用域
- 函数作用域
- 词法/静态作用域
- 闭包
- “this”对象和作用域
- 使用 call() 更改作用域
- 公共和私有作用域
范围
作用域是指代码的当前上下文。作用域可以是全局的,也可以是局部的。通过理解作用域,您将了解变量和函数/对象可以在哪里访问,并且您将能够更改代码的作用域。它使我们能够编写更易于维护的代码,并且能够更快地进行调试。
全局作用域
当我们开始编写任何 JavaScript 代码时,我们就处于全局作用域中。例如,当我们声明一个变量时,该变量就是全局定义的,这意味着它在脚本中的任何位置都可以访问。
示例
var app = 'Demo';
console.log(app);
function funcA() {
console.log("inside a function: ", app);
};
funcA();
解释
在上面的示例中,变量 app
是在全局作用域中定义的。正如您所看到的,该变量在全局作用域中被调用,也在函数内部被调用。因为变量是在全局级别定义的,所以它在脚本中的任何地方都可以访问。通常只有一个全局作用域。
控制台输出是
局部作用域
当您在全局作用域(顶层)或另一个函数(局部作用域)内定义一个函数时,该函数就有自己的局部作用域。外部函数的局部作用域可以被内部函数访问。
示例 1
var globalVar = "Global Scope";
var funcG = function () {
// Local scope for funcG()
var funcName = 'funcG';
console.log(funcName);
//Local scope can access the global scope
console.log(globalVar);
};
funcG();
//Global scope cannot access the local scope.
console.log(funcName);
解释
以上示例 1 的输出如下,您可以看到变量 funcName
在最后一行 console.log(funcName);
中是未定义的,因为它是在函数内的局部作用域中定义的,而全局变量 globalVar
可以在局部作用域内的 console.log(globalVar);
中访问。
示例 2
function funcOuter() {
var outerVar = 'local variable funcOuter();';
function funcInner(){
var innerVar = 'local variable funcInner()';
console.log('inside funcInner : ', innerVar);
console.log('inside funcInner : ', outerVar);
}
console.log(outerVar);
console.log(innerVar);
}
解释
以下是示例 2 的输出,其中 innerVar
是未定义的,因为它定义在 funcInner()
函数内部以及局部作用域内,外部函数无法访问。
同时,在 funcOuter
函数中定义的变量 outerVa
r 可以在 funcInner()
内部访问,因为外部局部作用域可以在内部局部作用域中访问。
函数作用域
作用域不是为每个语句创建的,而是为每个函数创建的。当使用 FOR
或 WHILE
语句时,不会创建新的作用域。函数即新作用域,这是基本规则。
示例
function funcLoop() {
for (i = 0; i < 5; i++) {
console.log('inside loop :', i);
}
console.log('outside loop :', i);
}
funcLoop();
解释
以上代码的输出如下,您可以看到变量 i
是在 FOR
语句内部定义的,并且在循环外部也可以访问,这证明了语句内部没有作用域。
词法/静态作用域
当您在一个函数内定义另一个函数时,内部函数可以访问外部函数的作用域。这称为词法作用域或静态作用域。
示例 1
var parentFunc = function () {
var param1 = 'parameter 1';
var childFunc = function () {
console.log('The parameter is :', param1);
}
childFunc();
console.log(param1);
}
parentFunc();
解释
上面代码的输出如下所示。您可以看到 childFunc()
可以访问定义在外部函数中的 var
param1
。
词法作用域是从最外层函数到最内层函数工作的。它不会向后工作。
示例 2
function outerMost() {
console.log(innerMost);
function outer() {
console.log(innerMost);
function inner() {
console.log(innerMost);
function innerMost() {
var innerMost = 'inner most';
}
}
}
}
outerMost();
解释
在示例 2 中,var<code>
innerMost 定义在 innerMost()
函数中,并且外部函数无法访问它。输出如下所示
闭包
闭包是一个返回两样东西的对象:一个函数和一个与之相关的环境。
闭包与词法作用域紧密相连。我们可以返回作用域内的内容,使其在父作用域中可用。
我们可以通过以下示例来演示。
示例
var echo = function (shout) {
var txt = 'Echoing back : ' + shout;
return function () {
console.log(txt);
}
}
var f1 = echo('Hello');
f1();
解释
上面的示例中的函数 echo()
返回一个使用 var txt
显示文本的函数。txt
定义在 echo()
函数内部。然而,正如您从以下输出中看到的,echo()
函数连同 var txt
的环境(作用域)一起被返回。
“this”对象和作用域
关键字‘this
’是 JavaScript 编程中非常重要的部分,理解‘this
’的值在代码中的不同位置是如何被赋值的,以及函数是如何被调用的,对于编写无错误的代码至关重要。
在最顶层的‘this
’的值是 window
对象。然而,‘this
’的值会因为以不同的方式调用函数而绑定到不同的值。我们可以通过一个示例轻松演示。
示例
var myFunc = function () {
console.log(this);
};
myFunc();
var myObj = {};
myObj.method1 = function () {
console.log(this);
};
myObj.method1();
<p class='scope'>Click</p>
<script type="text/javascript">
var p = document.querySelector('.scope');
var display = function () {
console.log(this);
};
p.addEventListener('click', display, false);
解释
第一个函数 myFunc()
返回 window
对象作为‘this
’,如下面的输出所示。
第二个函数 myObj.method1()
返回 myObj
对象作为‘this
’,因为 method1()
函数是 myObj
对象的一部分。输出如下所示
第三,display()
函数返回 <p>
元素作为‘this
’,因为 click()
函数是在 <p>
元素上调用的。输出如下所示
使用 call() 更改作用域
‘this
’的作用域可以使用我们代码中的 call()
函数来更改。
我们来看下面的例子
<ul>
<li>Red</li>
<li>Green</li>
<li>Yellow</li>
<li>Blue</li>
</ul>
<script type="text/javascript">
var listItems = document.querySelectorAll('ul li');
for (var l = 0; l < listItems.length; l++) {
console.log(this);
}
</script>
在‘for
’循环中,开发人员试图记录列表项的值。但实际上在控制台记录的值是 window
对象,因为在作用域中‘this
’指向 window
对象。控制台输出和浏览器输出如下
当然,我们可以像下面这样通过数组索引来获取列表项的值
console.log(listItems[l]);
为了演示如何通过更改上下文来更改作用域,我们将使用上述代码中的 call ()
方法。上面的代码使用 call ()
方法重写,我们可以看到它如何改变代码的上下文及其作用域。
var listItems = document.querySelectorAll('ul li');
for (var l = 0; l < listItems.length; l++) {
(function () {
console.log(this);
}).call(listItems[l]);
}
以上输出如下所示。这次,正如您所看到的,<li>
元素被返回作为‘this
’对象。通过这种方式,call()
函数在某些情况下非常强大。
公共和私有作用域
JavaScript 本身不像其他语言那样有 public
和 private
作用域,但我们可以使用模块模式来模拟它们。
让我们看下面的例子
var myModule = (function () {
var _myPrivateMethod = function () {
console.log('_myPrivateMethod()');
}
return {
methodOne: function () {
console.log('methodOne()');
}
};
})();
myModule.methodOne();
myModule._myPrivateMethod();
在上面的代码中,行 myModule._myPrivateMethod();<font color="#111111" face="Segoe UI"> </font>
会失败,因为函数 _myPrivateMethod()
是 private
方法,对 myModule
可访问。
然而,代码行 myModule.methodOne();
会成功,因为该方法通过 return
语句被设为 public
。
关注点
值得一读关于 bind()
和 apply()
,它们与 call()
类似,但略有不同。也值得详细阅读 module
模式,因为它是 Angular JS 的关键。