javascript中变量的范围是什么?它们在函数内部和外部的作用域是否相同?还是有关系吗?另外,如果变量是全局定义的,则将变量存储在哪里?
JavaScript中变量的范围是什么?
在EcmaScript5中,主要有两个范围,本地范围和全局范围,但是在EcmaScript6中,我们主要有三个范围,本地范围,全局范围和称为块范围的新范围。
块范围的示例是:-
for ( let i = 0; i < 10; i++)
{
statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}
我的理解是有3个范围:全局范围,全局可用;局部作用域,无论块如何,整个功能都可以使用;和块作用域,仅对使用它的块,语句或表达式可用。全局和局部作用域在函数内或外部用关键字“ var”指示,而块作用域用关键字“ let”指示。
对于那些相信只有全局和局部作用域的用户,请解释为什么Mozilla会有整个页面描述JS中块作用域的细微差别。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/let
试试这个奇怪的例子。在下面的示例中,如果a是初始化为0的数字,则将看到0,然后是1。除了a是一个对象,并且javascript会将f的指针(而不是其副本)传递给f1。结果是您两次都收到相同的警报。
var a = new Date();
function f1(b)
{
b.setDate(b.getDate()+1);
alert(b.getDate());
}
f1(a);
alert(a.getDate());
在JavaScript中,作用域有两种类型:
- 当地范围
- 全球范围
以下函数具有局部作用域变量carName
。而且该变量不能从函数外部访问。
function myFunction() {
var carName = "Volvo";
alert(carName);
// code here can use carName
}
以下类具有全局范围变量carName
。而且该变量可从类中的任何地方访问。
class {
var carName = " Volvo";
// code here can use carName
function myFunction() {
alert(carName);
// code here can use carName
}
}
JS中只有函数作用域。不阻止范围!您也可以看到正在起吊的东西。
var global_variable = "global_variable";
var hoisting_variable = "global_hoist";
// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);
if (true) {
// The variable block will be global, on true condition.
var block = "block";
}
console.log("global_scope: - block: " + block);
function local_function() {
var local_variable = "local_variable";
console.log("local_scope: - local_variable: " + local_variable);
console.log("local_scope: - global_variable: " + global_variable);
console.log("local_scope: - block: " + block);
// The hoisting_variable is undefined at the moment.
console.log("local_scope: - hoisting_variable: " + hoisting_variable);
var hoisting_variable = "local_hoist";
// The hoisting_variable is now set as a local one.
console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}
local_function();
// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);
现代Js,ES6 +,“ const
”和“ let
”
就像大多数其他主要语言一样,您应该对创建的每个变量使用块作用域。var
已经过时了。这使您的代码更安全,更可维护。
const
应该用于95%的情况。它使得变量引用无法更改。数组,对象和DOM节点属性可以更改,并且应该更改为const
。
let
应该用于期望重新分配的任何变量。这包括在for循环中。如果您在初始化后更改了值,请使用let
。
块作用域意味着该变量将仅在声明该变量的方括号内可用。这扩展到内部范围,包括在您的范围内创建的匿名函数。
全球范围:
全局变量就像全局明星一样(成龙,纳尔逊·曼德拉)。您可以从应用程序的任何部分访问它们(获取或设置值)。全局功能就像全局事件(新年,圣诞节)。您可以从应用程序的任何部分执行(调用)它们。
//global variable
var a = 2;
//global function
function b(){
console.log(a); //access global variable
}
当地范围:
如果您在美国,可能会认识臭名昭著的金·卡戴珊(她以某种方式设法制作了小报)。但是美国以外的人不会认出她。她是当地的明星,一定会进入她的领土。
局部变量就像局部恒星。您只能在范围内访问它们(获取或设置值)。局部函数就像局部事件-您只能在该作用域内执行(庆祝)。如果要从范围之外访问它们,则会得到参考错误
function b(){
var d = 21; //local variable
console.log(d);
function dog(){ console.log(a); }
dog(); //execute local function
}
console.log(d); //ReferenceError: dddddd is not defined
ALMOST只有两种类型的JavaScript范围:
- 每个var声明的范围都与最直接封闭的函数相关联
- 如果var声明没有封闭函数,则为全局范围
因此,除功能以外的任何块都不会创建新的作用域。这就解释了为什么for循环会覆盖外部作用域变量:
var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5
使用函数代替:
var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10
在第一个示例中,没有块作用域,因此最初声明的变量被覆盖。在第二个示例中,由于该函数而存在新作用域,因此最初声明的变量为SHADOWED,并且不会被覆盖。
就JavaScript作用域而言,几乎是您需要了解的所有内容,除了:
- try / catch仅为异常变量本身引入新作用域,其他变量没有新作用域
- 显然,with-clause是另一个例外,但是强烈建议不要使用with-clause(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)
因此,您可以看到JavaScript范围定义实际上非常简单,尽管并不总是直观的。需要注意的几件事:
- var声明被提升到范围的顶部。这意味着无论var声明发生在什么地方,对于编译器而言,好像var本身发生在顶部
- 合并同一范围内的多个var声明
所以这段代码:
var i = 1;
function abc() {
i = 2;
var i = 3;
}
console.log(i); // outputs 1
等效于:
var i = 1;
function abc() {
var i; // var declaration moved to the top of the scope
i = 2;
i = 3; // the assignment stays where it is
}
console.log(i);
这可能看起来与直觉相反,但是从命令式语言设计师的角度来看这是有道理的。
JavaScript只有两种类型的作用域:
- 全局范围全局范围不过是一个窗口级范围。这里,整个应用程序中都存在变量。
- 功能范围:在具有
var
关键字的函数中声明的变量具有功能范围。
每当调用函数时,都会创建变量作用域对象(并将其包含在作用域链中),然后在JavaScript中跟随变量。
a = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
范围链->
- 窗位-
a
和outer
功能是在作用域链顶层。 - 当外部函数称为新函数
variable scope object
(并包含在作用域链中)并b
在其中添加了变量时。
现在,当a
需要一个变量时,它首先搜索最近的变量范围,如果不存在该变量,则将其移到变量范围链的下一个对象。
我发现许多不熟悉JavaScript的人很难理解,语言默认情况下可以继承,并且函数作用域是迄今为止唯一的作用域。我提供了我在去年年底编写的名为JSPretty的美化工具的扩展。要素颜色在代码中作用于作用域,并且始终将颜色与该作用域中声明的所有变量关联。当一个颜色的变量来自一个范围时,在另一范围中使用闭包以可视方式演示。
请尝试以下功能:
观看演示:
在以下位置查看代码:
- http://prettydiff.com/lib/jspretty.js
- https://github.com/austincheney/Pretty-Diff/blob/master/lib/jspretty.js
当前,该功能支持深度为16的嵌套函数,但当前不为全局变量着色。
The idea of scoping in JavaScript when originally designed by Brendan Eich came from the HyperCard scripting language HyperTalk.
In this language, the displays were done similar to a stack of index cards. There was a master card referred to as the background. It was transparent and can be seen as the bottom card. Any content on this base card was shared with cards placed on top of it. Each card placed on top had its own content which took precedence over the previous card, but still had access to the prior cards if desired.
This is exactly how the JavaScript scoping system is designed. It just has different names. The cards in JavaScript are known as Execution ContextsECMA. Each one of these contexts contains three main parts. A variable environment, a lexical environment, and a this binding. Going back to the cards reference, the lexical environment contains all of the content from prior cards lower in the stack. The current context is at the top of the stack and any content declared there will be stored in the variable environment. The variable environment will take precedence in the case of naming collisions.
The this binding will point to the containing object. Sometimes scopes or execution contexts change without the containing object changing, such as in a declared function where the containing object may be window
or a constructor function.
These execution contexts are created any time control is transferred. Control is transferred when code begins to execute, and this is primarily done from function execution.
So that is the technical explanation. In practice, it is important to remember that in JavaScript
- Scopes are technically "Execution Contexts"
- Contexts form a stack of environments where variables are stored
- The top of the stack takes precedence (the bottom being the global context)
- Each function creates an execution context (but not always a new this binding)
将其应用于此页面上的先前示例之一(5.“结束”),可以遵循执行上下文堆栈。在此示例中,堆栈中有三个上下文。它们由外部上下文定义,由var 6调用的立即调用函数中的上下文,以及在var 6的立即调用的函数内部返回的函数中的上下文。
i)外部环境。它具有a = 1的可变环境
ii)IIFE上下文,它具有a = 1的词法环境,但是a = 6的变量环境在堆栈中具有优先权
iii)返回的函数上下文,它具有词法a = 6的环境,这是调用时警报中引用的值。
据我所知,关键是Javascript具有功能级别范围,而不是更常见的C块范围。
在“ Javascript 1.7”(Mozilla对Javascript的扩展)中,还可以使用let
语句声明块范围变量:
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
Javascript使用范围链为给定功能建立范围。通常有一个全局范围,并且定义的每个函数都有其自己的嵌套范围。在另一个函数中定义的任何函数都具有与外部函数链接的局部作用域。定义范围的始终是源中的位置。
范围链中的元素基本上是一个Map,具有指向其父范围的指针。
解析变量时,javascript从最内部的范围开始并向外搜索。
JavaScript有两种作用域。
全局范围:在全局范围内声明的变量可以在程序中的任何位置非常平稳地使用。例如:
功能范围或局部范围:在此范围中声明的变量只能在其自己的函数中使用。例如: