javascript中变量的范围是什么?它们在函数内部的作用域与在函数外部的作用域相同吗?或者这有关系吗?此外,如果变量是全局定义的,那么变量存储在哪里?
当前回答
在“Javascript 1.7”(Mozilla对Javascript的扩展)中,还可以使用let语句声明块范围变量:
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
其他回答
下面是一个示例:
<script>
var globalVariable = 7; //==window.globalVariable
function aGlobal( param ) { //==window.aGlobal();
//param is only accessible in this function
var scopedToFunction = {
//can't be accessed outside of this function
nested : 3 //accessible by: scopedToFunction.nested
};
anotherGlobal = {
//global because there's no `var`
};
}
</script>
您将需要调查闭包,以及如何使用它们创建私有成员。
我的理解是有三个范围:全球范围,全球可用;局部范围,无论块如何,整个功能都可用;和块范围,仅对使用它的块、语句或表达式可用。全局和局部作用域用关键字“var”表示,无论是在函数内还是在函数外,块作用域都用关键字“let”表示。
对于那些认为只有全局和局部范围的人,请解释为什么Mozilla会有一个完整的页面来描述JS中块范围的细微差别。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
ES5及更早版本:
Javascript中的变量最初(在ES6之前)是函数范围的。词汇范围这一术语意味着您可以通过“查看”代码来查看变量的范围。
用var关键字声明的每个变量都在函数的作用域内。但是,如果在该函数中声明了其他函数,则这些函数将可以访问外部函数的变量。这称为作用域链。其工作方式如下:
当一个函数试图解析一个变量值时,它首先查看自己的范围。这是函数体,即花括号{}之间的所有内容(除了在此范围内的其他函数内的变量)。如果它在函数体中找不到变量,它将爬上链,查看函数中定义函数的变量范围。这就是词法作用域的含义,我们可以在代码中看到这个函数的定义,因此可以通过查看代码来确定作用域链。
例子:
//全局范围var foo=“全局”;var bar=“全局”;var foobar=“全局”;函数outerFunc(){//outerFunc范围var foo='outerFunc';var foobar='outerFunc';innerFunc();函数innerFunc(){//innerFunc范围var foo='innerFunc';console.log(foo);console.log(bar);console.log(foobar);}}outerFunc();
当我们试图将变量foo、bar和foobar记录到控制台时,会发生以下情况:
我们尝试将foo记录到控制台,foo可以在函数innerFunc本身中找到。因此,foo的值被解析为字符串innerFunc。我们试图将bar记录到控制台,但在函数innerFunc本身中找不到bar。因此,我们需要攀登范围链。我们首先查看定义了函数innerFunc的外部函数。这是outerFunc函数。在outerFunc的作用域中,我们可以找到变量栏,其中包含字符串“outerFun”。在innerFunc中找不到foobar。因此,我们需要将作用域链升级到innerFunc作用域。在这里也找不到它,我们爬到了另一个层次的全局范围(即最外面的范围)。我们在这里找到了保存字符串“global”的变量foobar。如果在攀爬范围链之后没有找到变量,JS引擎将抛出referenceError。
ES6(ES 2015)及以上版本:
词汇范围和范围的概念仍然适用于ES6。然而,引入了一种声明变量的新方法。具体如下:
let:创建块范围变量const:创建一个必须初始化且不能重新分配的块范围变量
var和let/cont之间最大的区别是var是函数范围的,而let/cont是块范围的。下面是一个例子来说明这一点:
let letVar=“全局”;var varVar=“全局”;函数foo(){if(真){//用let声明的这个变量的作用域是if块,块作用域设letVar=5;//用let声明的这个变量的作用域是函数块,函数作用域var varVar=10;}console.log(letVar);console.log(varVar);}foo();
在上面的示例中,letVar记录全局值,因为用let声明的变量是块范围的。它们不再存在于各自的块之外,因此无法在if块之外访问变量。
据我理解,关键是Javascript具有函数级作用域,而不是更常见的C块作用域。
这是一篇关于这个主题的好文章。
老式JavaScript
传统上,JavaScript实际上只有两种类型的作用域:
全局范围:从应用程序开始,变量在整个应用程序中都是已知的(*)函数作用域:变量在声明它们的函数中是已知的,从函数开始(*)
我不会详细说明这一点,因为已经有许多其他答案可以解释这一差异。
现代JavaScript
最新的JavaScript规范现在还允许第三个范围:
块作用域:标识符从其声明的作用域的顶部开始“已知”,但在其声明的行之后才能分配或取消引用(读取)。这一过渡时期被称为“时间死区”
如何创建块范围变量?
传统上,您可以这样创建变量:
var myVariable = "Some text";
块范围变量的创建方式如下:
let myVariable = "Some text";
那么,功能范围和块范围之间的区别是什么?
要了解功能范围和块范围之间的区别,请考虑以下代码:
// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here
function loop(arr) {
// i IS known here, but undefined
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( var i = 0; i < arr.length; i++ ) {
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
for( let j = 0; j < arr.length; j++ ) {
// i IS known here, and has a value
// j IS known here, and has a value
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
};
// i IS known here, and has a value
// j IS NOT known here
// k IS known here, but has a value only the second time loop is called
// l IS NOT known here
}
loop([1,2,3,4]);
for( var k = 0; k < arr.length; k++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
};
for( let l = 0; l < arr.length; l++ ) {
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS known here, and has a value
};
loop([1,2,3,4]);
// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here
在这里,我们可以看到变量j只在first for循环中知道,而在前后都不知道。然而,我们的变量i在整个函数中是已知的。
此外,考虑到块范围的变量在声明之前是未知的,因为它们没有被提升。也不允许在同一块内重新声明同一块范围内的变量。这使得块作用域变量比全局或函数作用域变量更不容易出错,这些变量被提升,在多个声明的情况下不会产生任何错误。
现在使用块范围变量安全吗?
今天使用它是否安全取决于您的环境:
如果您正在编写服务器端JavaScript代码(Node.js),则可以安全地使用let语句。如果您正在编写客户端JavaScript代码并使用基于浏览器的转译器(如Traceur或babel standalone),则可以安全地使用let语句,但是您的代码在性能方面可能不是最佳的。如果您正在编写客户端JavaScript代码并使用基于节点的转译器(如traceur shell脚本或Babel),则可以安全地使用let语句。而且,由于您的浏览器将只知道转译的代码,因此性能缺陷应该受到限制。如果您正在编写客户端JavaScript代码,而不使用转译器,则需要考虑浏览器支持。以下是一些根本不支持let的浏览器:Internet explorer 10及以下Firefox 43及以下Safari 9及以下Android浏览器4及以下版本Opera 27及以下Chome 40及以下Opera Mini和黑莓浏览器的任何版本
如何跟踪浏览器支持
有关阅读此答案时哪些浏览器支持let语句的最新概述,请参阅“我可以使用”页面。
(*)全局和函数范围的变量可以在声明之前初始化和使用,因为JavaScript变量被提升。这意味着声明总是位于范围的顶部。
推荐文章
- Babel 6改变了它导出默认值的方式
- 如何配置历史记录?
- ES6模板文字可以在运行时被替换(或重用)吗?
- [Vue警告]:找不到元素
- 可以在setInterval()内部调用clearInterval()吗?
- AngularJS控制器的生命周期是什么?
- 无法读取未定义的属性“msie”- jQuery工具
- 我的蛋蛋怎么不见了?
- JavaScript中的排列?
- JavaScript中有睡眠/暂停/等待功能吗?
- 如何禁用文本选择使用jQuery?
- 如何停止事件冒泡复选框点击
- 如何在PHP中截断字符串最接近于一定数量的字符?
- 向对象数组添加属性
- 如何在Redux应用程序中动态加载代码分割的减速器?