-
2021-06-15 15:45:12更多相关内容
-
图解javascript作用域链
2020-12-02 14:18:41先来一段简单的javascript代码: ...1.javascript引擎会在页面加载脚本被执行时为每个函数创建一个作用域(执行上下文)及作用域链。 2.javascript引擎在产生这些作用域后,会创建一个堆栈。 3.将onload对应的 -
JS作用域链详解
2022-02-21 17:45:42在 JS 中作用域分为全局作用域和函数作用域,另外函数作用域可以互相嵌套。在下面的例子中,存在着全局作用域 fn 作用域和 bar作用域,他们相互嵌套。 <script> var a = 1; var b = 2; function fn(x...目录
1、作用域
作用域是一套规则,用来确定在何处以及如何查找标识符。在 JS 中作用域分为全局作用域和函数作用域,另外函数作用域可以互相嵌套。在下面的例子中,存在着全局作用域 fn 作用域和 bar作用域,他们相互嵌套。<script> var a = 1; var b = 2; function fn(x) { var a = 10; function bar(x) { var a = 100; b = x + a; return b; } bar(20); bar(200); } fn(0); </script>
2、作用域链
各个作用域的嵌套关系组成一条作用域链。例子中 bar 函数的作用域链式 bar -> fn -> 全局, fn函数保存在作用域链式 fn -> 全局作用域链主要是进行标识符(变量和函数)的查询,标识符解析就是沿着作用域链一级一级的搜索标识符的过程,而作用域链就是保证对变量和函数的有序访问。(1)如果自身作用域中声明该变量,则无需使用作用域链在上面的例子中,如果要在 bar 函数中查询变量 a ,则直接使用 LHS 查询,赋值为 100 即可。(2)如果自身作用域中未声明该变量,则需要使用作用域链进行查找3、自由变量
在当前作用域中存在但未在当前作用域中声明的变量叫自由变量 。如 bar 函数中的变量 b 就是一个自由变量。注意:在程序中如果存在自由变量,那么一定存在作用域链。4、执行环境
执行环境( execution context )也叫上下文、执行上下文环境。每个执行环境都有一个与之关联的变量对象(variable object ),环境中定义的所有变量和函数都保存在这个对象中。JavaScript 代码执行的环境非常重要,而执行环境可以归纳为以下三种:(1)全局代码( Global Code ) : 代码首次执行时的默认环境(2)函数代码( Function Code ) : 程序执行到函数体内时(3)Eval 函数代码( Eval Code ) : 内置 Eval 函数计算的字符串<script> var a = 1; var b = 2; function fn(x) { var a = 10; function bar(x) { var a = 100; b = x + a; return b; } bar(20); bar(200); } fn(0); </script>
以上述代码为例来说明:在 fn 这个函数中保存有 x、a、bar 环境变量,另外还保存了this、arguments 环境变量。
5、执行流程
所谓执行流,就是程序执行的顺序。它的执行顺序为:先执行 var a = 1 ,然后再执行 var b = 2 ,接下来执行 fn(0) ,再执行 var a = 10 ,再执行 bar(20),再执行 var a = 100 ,再执行 b = x + a ,再执行 return b ,再执行 bar(200) ,再执行 var a = 100,再执行 b = x + a ,再执行 return b ,最后程序结束。JavaScript 的运行时流程图如下:6、执行栈
当打开网页或浏览器时, 宿主环境 会将代码传递给 引擎 去执行,引擎首先会创建一个全局执行环境。全局环境中的代码自上而下有顺序的执行,当遇到一个函数时,函数的环境被创建,函数中的代码开始执行;而在函数执行之后,控制权又返还给之前的环境。 ES 这种类似于 " 栈 " 的控制机制,称为 执行栈 。简单的说,执行环境栈就是一个压栈和出栈的过程。(1) 宿主环境:浏览器或者 Node 环境。(2)引擎:从头到尾负责整个 JavaScript 代码的编译及执行过程。(3)栈:一种遵循 " 后进先出 " 原则的有序数据集合,可以简单理解为使用 push() 和 pop() 操作数组。下面以示例来进行说明:<script> console.log(1); function pFn() { console.log(2); (function cFn() { console.log(3); }()); console.log(4); } pFn(); console.log(5); // 输出:1 2 3 4 5 </script>
示意图:
-
理解JavaScript作用域和作用域链
2021-03-02 00:10:16作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript。任何程序... -
浅析JavaScript作用域链、执行上下文与闭包
2020-10-22 21:34:46JavaScript 采用词法作用域(lexical scoping),函数执行依赖的变量作用域是由函数定义的时候决定,而不是函数执行的时候决定,通过本文给大家介绍JavaScript作用域链、执行上下文与闭包相关知识,感兴趣的朋友一起... -
JavaScript 作用域 和作用域链
2021-01-20 13:29:41JavaScript 作用域 和作用域链作用域作用域链 作用域 作用域就是变量与函数的可访问范围,作用域控制着变量与函数的可见性和生命周期。换句话说,作用域决定了代码区块中变量和其他资源的可见性。 function fun() { ... -
javascript作用域链(Scope Chain)用法实例解析
2020-10-23 05:57:17主要介绍了javascript作用域链(Scope Chain)用法,结合实例形式较为详细的分析了javascript作用域链(Scope Chain)的概念、功能与相关使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下 -
javascript 嵌套的函数(作用域链)
2020-10-29 07:25:40当你进行函数的嵌套时,要注意实际上作用域链是发生变化的,这点可能看起来不太直观。你可把下面的代码置入firebug监视值的变化。 -
简单理解javascript作用域链
2019-03-19 22:04:42简单理解js的作用域链(scope chain),就好比一个套娃,最外面的总是当前执行的函数,第二层套娃是最外面(当前执行环境)的外部环境,第三层套娃又是第二层套娃(执行环境)的外部环境,依次类推,最里面的一般是...套娃思维:
简单理解js的作用域链(scope chain),就好比一个套娃,最外面的总是当前执行的函数,第二层套娃是最外面(当前执行环境)的外部环境,第三层套娃又是第二层套娃(执行环境)的外部环境,依次类推,最里面的一般是全局执行环境,js在搜索变量时总是从套娃的最外面(当前执行环境)搜索到最里面(全局执行环境)。
当然,既然是链,其中每一个单元,都是一个作用域或者成为执行环境,js背后对每一个这个单元或称执行环境,都约定有一个和其对应的对象,用来保存在这个环境中定义的变量和函数,这个对象就叫变量对象(variable object).
通过例子来理解:
首先分析一下下面这段代码的作用域链
var color="blue"; function changeColor(){ if(color=="blue"){ color="red" } else{ color="blue" }}; changeColor(); console.log("Color now is "+color);
这段代码的执行结果是
Color now is red
分析一下作用域及执行过程:
首先,这段代码有两个作用域,一个是全局作用域,另一个是函数changeColor()的作用域。当前的执行环境是changeColor(),他处于作用域链最顶端,此时因为执行环境是一个函数,所以将其活动对象作为它的变量对象(variable object),最开始的活动对象只保存一个arguments对象(js函数参数)
全局执行环境出于最低端,它对应的**变量对象(variable object)**保存一个变量
var color="blue";
执行这段代码时,首先执行函数中的部分,由于变量
color
并不在当前的执行环境中,所以js会沿着作用域链往下搜索,当在全局作用域链中找到已声明和赋值的变量color
时,继续执行函数中的判断语句,此时color
为blue
,所以将color重新赋值为red
,这次赋值以后,全局作用域中的变量color
就成了red
,所以在全局作用域中执行最后一行代码,结果为Color now is red
-
JavaScript作用域、闭包、对象与原型链概念及用法实例总结
2020-10-18 04:39:47主要介绍了JavaScript作用域、闭包、对象与原型链,结合实例形式总结分析了javascript中变量与函数的作用域、闭包、对象、原形链相关概念、用法及注意事项,需要的朋友可以参考下 -
JavaScript作用域链实例详解
2020-12-09 16:54:21本文实例讲述了JavaScript作用域链。分享给大家供大家参考,具体如下: 跟其他语言一样,变量和函数的作用域揭示了这些变量和函数的搜索路径。对于JavaScript而言,理解作用域更加重要,因为在JavaScript中,作用域... -
Javascript作用域和作用域链原理解析
2020-11-21 02:11:52作用域和作用域链在Javascript和很多其它的编程语言中都是一种基础概念。但很多Javascript开发者并不真正理解它们,但这些概念对掌握Javascript至关重要。 正确的去理解这个概念有利于你去写更好,更高效和更简洁的... -
通过函数作用域和块级作用域看javascript的作用域链
2021-01-19 18:25:57在ES6之前,javascript...在了解作用域链之前,我们先了解一个执行期上下文的概念。 执行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象(即AO或GO),一个执行期上下文定义了一个函数的执行环境,函数 -
理解Javascript的作用域和作用域链
2021-03-30 01:11:58作用域和作用域链在Javascript和很多其它的编程语言中都是一种基础概念。但很多Javascript开发者并不真正理解它们,但这些概念对掌握Javascript至关重要。 正确的去理解这个概念有利于你去写更好,更高效和更简洁的...目录
正文
作用域和作用域链在Javascript和很多其它的编程语言中都是一种基础概念。但很多Javascript开发者并不真正理解它们,但这些概念对掌握Javascript至关重要。
正确的去理解这个概念有利于你去写更好,更高效和更简洁的代码,让你成为一个更优秀的Javascript开发者。
因此,在本文中,我将会向大家解释清楚什么是作用域和作用域链,以及Javascript引擎在内部是如何通过它们操作和查找变量的。
事不宜迟,正文开始 :)
什么是作用域
Javascript中的作用域说的是变量的可访问性和可见性。也就是说整个程序中哪些部分可以访问这个变量,或者说这个变量都在哪些地方可见。
为什么作用域很重要
- **作用域最为重要的一点是安全。**变量只能在特定的区域内才能被访问,有了作用域我们就可以避免在程序其它位置意外对某个变量做出修改。
- **作用域也会减轻命名的压力。**我们可以在不同的作用域下面定义相同的变量名。
作用域的类型
Javascript中有三种作用域:
- 全局作用域;
- 函数作用域;
- 块级作用域;
1. 全局作用域
任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问。例如:
// 全局变量 var greeting = 'Hello World!'; function greet() { console.log(greeting); } // 打印 'Hello World!' greet(); 复制代码
2. 函数作用域
函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问。例如:
function greet() { var greeting = 'Hello World!'; console.log(greeting); } // 打印 'Hello World!' greet(); // 报错: Uncaught ReferenceError: greeting is not defined console.log(greeting); 复制代码
3. 块级作用域
ES6引入了
let
和const
关键字,和var
关键字不同,在大括号中使用let
和const
声明的变量存在于块级作用域中。在大括号之外不能访问这些变量。看例子:{ // 块级作用域中的变量 let greeting = 'Hello World!'; var lang = 'English'; console.log(greeting); // Prints 'Hello World!' } // 变量 'English' console.log(lang); // 报错:Uncaught ReferenceError: greeting is not defined console.log(greeting); 复制代码
上面代码中可以看出,在大括号内使用
var
声明的变量lang是可以在大括号之外访问的。使用var
声明的变量不存在块级作用域中。作用域嵌套
像Javascript中函数可以在一个函数内部声明另一个函数一样,作用域也可以嵌套在另一个作用域中。请看例子:
var name = 'Peter'; function greet() { var greeting = 'Hello'; { let lang = 'English'; console.log(`${lang}: ${greeting} ${name}`); } } greet(); 复制代码
这里我们有三层作用域嵌套,首先第一层是一个块级作用域(
let
声明的),被嵌套在一个函数作用域(greet
函数)中,最外层作用域是全局作用域。词法作用域
词法作用域(也叫静态作用域)从字面意义上看是说作用域在词法化阶段(通常是编译阶段)确定而非执行阶段确定的。看例子:
let number = 42; function printNumber() { console.log(number); } function log() { let number = 54; printNumber(); } // Prints 42 log(); 复制代码
上面代码可以看出无论
printNumber()
在哪里调用console.log(number)
都会打印42
。动态作用域不同,
console.log(number)
这行代码打印什么取决于函数printNumber()
在哪里调用。如果是动态作用域,上面
console.log(number)
这行代码就会打印54
。使用词法作用域,我们可以仅仅看源代码就可以确定一个变量的作用范围,但如果是动态作用域,代码执行之前我们没法确定变量的作用范围。
像C,C++,Java,Javascript等大多数编程语言都支持静态作用域。Perl 既支持动态作用域也支持静态作用域。
作用域链
当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域。
如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错。
例如:
let foo = 'foo'; function bar() { let baz = 'baz'; // 打印 'baz' console.log(baz); // 打印 'foo' console.log(foo); number = 42; console.log(number); // 打印 42 } bar(); 复制代码
当函数
bar()
被调用,Javascript引擎首先在当前作用域下寻找变量baz
,然后寻找foo变量但发现在当前作用域下找不到,然后继续在外部作用域寻找找到了它(这里是在全局作用域找到的)。然后将
42
赋值给变量number
。Javascript引擎会在当前作用域以及外部作用域下一步步寻找number变量(没找到)。如果是在非严格模式下,引擎会创建一个
number
的全局变量并把42
赋值给它。但如果是严格模式下就会报错了。**结论:**当使用一个变量的时候,Javascript引擎会循着作用域链一层一层往上找该变量,直到找到该变量为止。
作用域和作用域链是如何工作的
以上内容已经讲解了作用域,作用域的类型,现在让我们看下Javascript引擎是如何确定变量的作用域链和如何去查找变量的。
要想理解Javascript是如何进行变量查找的,必须要了解Javascript中词法环境这个概念
什么是词法环境
所谓词法环境就是一种标识符—变量映射的结构(这里的标识符指的是变量/函数的名字,变量是对实际对象[包含函数和数组类型的对象]或基础数据类型的引用)。
简单地说,词法环境是Javascript引擎用来存储变量和对象引用的地方。
注意:不要混淆了词法环境和词法作用域,词法作用域是在代码编译阶段确定的作用域(译者注:一个抽象的概念),而词法环境是Javascript引擎用来存储变量和对象引用的地方(译者注:一个具象的概念)。
一个词法环境就像下面这样:
lexicalEnvironment = { a: 25, obj: <ref. to the object> } 复制代码
只有当该作用域的代码被执行的时候,引擎才会为那个作用域创建一个新的词法环境。词法环境还会记录所引用的外部词法环境(即外部作用域)。例:
lexicalEnvironment = { a: 25, obj: <ref. to the object> outer: <outer lexical environemt> } 复制代码
Javascript引擎是如何进行变量查找的
现在我们已经知道了作用域,作用域链和词法环境的概念,现在让我们看下Javascript引擎是如何利用词法环境来确定作用域和作用域链的。
结合例子我们来理解上面的这些概念:
let greeting = 'Hello'; function greet() { let name = 'Peter'; console.log(`${greeting} ${name}`); // Hello Peter } greet(); { let greeting = 'Hello World!' console.log(greeting); // Hello World! } 复制代码
上述代码加载后,首先会创建一个全局词法环境,其中包含在全局范围内声明的变量和函数。像下面这样:
globalLexicalEnvironment = { greeting: 'Hello' greet: <ref. to greet function> outer: <null> } 复制代码
这里的
outer
字段(也就是外部词法环境)被设置为了null
,是因为全局词法环境已经是最顶层的词法环境了。然后,我们调用了
greet()
函数,然后一个新的词法环境会被被创建:functionLexicalEnvironment = { name: 'Peter' outer: <globalLexicalEnvironment> } 复制代码
这里的
outer
字段被设置为了globalLexicalEnvironment
,是因为他的外部作用域就是全局作用域。然后,执行console.log(`${greeting} ${name}`)这行代码,Javascript引擎首先在当前函数的词法环境中寻找变量
greeting
和name
,但只找到了name
,没找到greeting
。然后继续在上层的词法环境中找greeting
(这里是全局作词法环境)。最后在全局词法环境中找到了greeting
。紧接着执行那段在大括号里的代码,为这个块级创建一个新的词法环境。如下:
blockLexicalEnvironment = { greeting: 'Hello World', outer: <globalLexicalEnvironment> } 复制代码
然后执行
console.log(greeting)
这行代码,首先在本层词法环境中找greeting
,OK,找到,结束。此时就不会再去外部作用域(这里是全局作用域)寻找该变量了。**注意:**只有
let
和const
声明变量才会创建一个新的词法环境存储,使用var
声明的变量会被存储在当前块(大括号)所在的词法环境中(全局词法环境或是函数词法环境中)。**结论:**当一个变量被使用时,Javascript引擎会首先在当前的词法环境中进行寻找,如果找不到就找上层词法环境中寻找,直到找到为止。
结论
作用域就是一个变量可访问和可见的区域,和函数一样,Javascript的作用域也可以嵌套,Javascript引擎会层层遍历作用域来寻找用到的变量。
Javascript使用词法作用域,这意味着变量的作用在编译阶段就会被确定。
Javascript引擎在程序执行期间使用词法环境来存储变量和函数。
作用域和作用域链是Javascript中的基础概念,理解作用域和作用域链能让你成为一个更优秀的Javascript开发者。
-
详解JavaScript作用域和作用域链
2020-12-12 22:42:54虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,本文我会尽我所能用最简单的方式来解释作用域和作用域链,希望大家有所收获! 作用域(Scope) 1. 什么是作用域 作用域是在运行时代码中的某些特定部分中... -
JavaScript作用域链使用介绍
2020-11-30 21:54:07之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时候发现作用... -
JavaScript作用域链示例分享
2020-10-25 21:05:23作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。今天这篇文章对JavaScript作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript。 -
JavaScript 作用域链解析
2020-10-25 06:22:42一直对Js的作用域有点迷糊,今天偶然读到Javascript权威指南,立马被吸引住了,写的真不错。我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫。谢谢心得吧 -
关于Javascript作用域链的八点总结
2020-12-10 14:40:501. JavaScript函数的作用域链分为定义时作用域链和运行时作用域链; 2.函数被定义的时候,它有一个属性[[scope]]标明它的定义作用域链,定义时作用域链[[scope]]遵守这样的规则:一个函数的定义时作用域链[[scope]]... -
javascript作用域链与执行环境详解
2020-12-11 04:40:32作用域、作用域链、执行环境、执行环境栈以及this的概念在javascript中非常重要,本人经常弄混淆,这里梳理一下; 局部作用域函数内部的区域,全局作用域就是window; 作用域链取决于函数被声明时的位置,解析...