你要了解的 JavaScript —— Scope
栏目: JavaScript · 发布时间: 5年前
内容简介:在JavaScript中,Scope(作用域)是一个非常重要的概念,对很多刚接触JavaScript的开发者来说,这个概念理解起来并不容易。本文的目的就是对JavaScript Scope的知识点做一次梳理,希望通过本文能帮助你更好的理解Scope。在JavaScript中,Scope一般译为如果一个变量定义在任何函数或花括号
在JavaScript中,Scope(作用域)是一个非常重要的概念,对很多刚接触JavaScript的开发者来说,这个概念理解起来并不容易。本文的目的就是对JavaScript Scope的知识点做一次梳理,希望通过本文能帮助你更好的理解Scope。
#Scope到底是什么?
在JavaScript中,Scope一般译为 作用域 ,可以通俗易懂的理解为:作用域 限定 了在某个 特定范围 内可以得到的 变量、函数和对象等资源 。这里注意几个关键点,首先作用域起着限定作用,其次它限定在某个特定范围,超过这个范围是不被允许的,最后它规定了可以得到什么,可以得到包括变量、函数声明和对象等。Scope可以分为Global Scope(全局作用域)和 Local Scope(局部作用域)。
#Global Scope
如果一个变量定义在任何函数或花括号 {}
之外,那么这个变量就处于Global Scope之中,这个变量称为全局变量。
// global scope let name = '老王' function alertName() { alert(name) } 复制代码
定义在Global Scope中的变量,可以在代码中的任何位置使用。尽管这样看起来很酷,但是还是不建议这么做。看下面的代码。
代码片段一:
// global scope let name = '老王' let name = '老李' // Uncaught SyntaxError: Identifier 'name' has already been declared 复制代码
代码片段二:
// global scope var name = '老王' var name = '老李' console.log(name) // 老李 复制代码
代码片段一出现了报错,通过 let
或者 const
定义了一个变量,然后再次定义同名变量,则会提示报错信息,这是因为产生了 命名冲突
。尽管代码片段二没有出现报错,但是通过 var
定义了一个变量,然后再次定义同名变量,则变量的值被覆盖了。
事实上,定义在Global Scope中的变量和函数越多,产生命名冲突的风险也就越大。由此可见,定义变量的时候应该尽可能的定义在Local Scope中,而非Global Scope中,避免污染全局命名空间。
#Local Scope
如果一个变量定义在函数或者花括号 {}
之中,那么其只能被一部分特定的代码所使用,我们可以认为它处于Local Scope中,这个变量可以称为局部变量。
Local Scope又包括Function Scope(函数作用域)和Block Scope(块级作用域)。先来看Function Scope。
Function Scope
在很长一段时间里,在JavaScript中声明一个变量,只能通过 var
来声明,在函数中通过 var
声明的变量是作用在Function Scope之中的。
// global scope function showName() { // function scope var name = '老王' if (true) { console.log(name) } } function getName() { // function scope return name } console.log(name) // undefined showName() // 老王 getName() // undefined 复制代码
通过以上代码可以发现,在getName这个函数中,没有声明 name
变量,但是它也无法获取 showName
中的 name
变量,这是因为在没有任何关系的函数之间,函数的Function Scope之间是相互隔离的。同时也可以发现在Global Scope中也是无法获取 showName
中的 name
变量的,这是因为 name
变量被限定在Function Scope之中。既然Global Scope无法获取Function Scope中的变量,那么Function Scope可不可以获取Global Scope中的变量呢?
// global scope function showName() { // function scope if (true) { console.log(name) } } var name = '老王' console.log(name) // 老王 showName() // 老王 复制代码
通过以上代码可知,在JavaScript中,子作用域可以获取父级作用域中的变量和函数声明,反之则不行。
Hoisting(变量提升)
在JavaScript中,有一个特别的现象,在函数开始执行的阶段,JavaScript Engine(JavaScript引擎)会将通过 var
声明的变量提升到函数的最顶端(其实函数的声明也会),这就是所谓的Hoisting。
// global scope name = '老王' age = 40 var name var age 复制代码
以上代码等价于
// global scope var name; var age; name = '老王'; age = 40; 复制代码
值得注意的是变量提升只会提升变量的声明,变量的赋值并不会提升,变量的赋值仍要等到代码执行到赋值语句的位置才会赋值。
// function scope console.log(name) // undefined name = '老王' console.log(name) // 老王 var name console.log(name) // 老王 复制代码
还有一种情况是需要避免出现的,那就是如果定义在函数中的变量没有使用 var
或者 let
和 const
声明,在非严格模式( use strict
)下,变量将提升为全局变量。
// global scope function showName() { console.log(name) name = '老王' console.log(name) } showName() // 老王, 老王 console.log(name) // 老王 name = '老李' showName() // 老李, 老王 console.log(name) // 老李 复制代码
这样 name
变量将可以被随意修改,可能会产品意想不到的bug。
Block Scope
在ES6出现之前,JavaScript并没有严格意义上的Block Scope,这让很多有其他语言开发经验的开发者感到很困惑。除了不推荐使用的 eval
和 with
,只有 try/catch
中的 catch
块拥有Block Scope。
try { alert(age) var name = '老王' } catch(err) { console.log(err) // ReferenceError: age is not defined } console.log(name) // 老王 console.log(err) // Uncaught ReferenceError: err is not defined 复制代码
由此可见, err
变量只限定在 catch
的Block Scope之中,而 try
则没有自己Block Scope。
ES6的发布为广大JavaScript开发者带来了全新的变量声明方式, let
和 const
,其中 let
和 var
一样,都是用来声明变量,而 const
则是用来声明常亮,顾名思义,其值是不可改变的。这里要重点说明的是, let
和 const
是作用于Block Scope的。
// global scope function showName() { // function scope var name1 = '老王' if (true) { // block scope let name2 = '老李' const name3 = '老赵' console.log(name3) // 老赵 } console.log(name1) // 老王 console.log(name2) // ReferenceError: name2 is not defined } showName() 复制代码
通过以上代码发现,在Block Scope之外, console.log
是无法获取 name2
和 name3
的,这就是Block Scope的限制。
JavaScript是自带Garbage collection(垃圾回收)机制的,我们实际开发过程不用特别关心内存回收的问题,因为JavaScript Engine已经帮我们处理好了。但在开发大型应用的时候,性能问题会逐渐凸显,关于性能,其实涉及到很多方面,这里不一一细说,其中有一点是我们可以利用Block Scope来提高垃圾回收的效率。
既然在Block Scope之外,代码是无法获取其中的变量的,Garbage collection一旦发现变量或对象没有被引用,就会将内存及时的回收以备他用。所以利用这个机制,我们可以优化部分代码的书写方式。
// global scope var name = '老王' { // block scope let age = 40 console.log(age) // 40 } console.log(name) // 老王 复制代码
通过加上花括号,来创建一个Block Scope,一旦代码执行结束,Block Scope中的变量将无法获取,那么它就会被及时回收。
接下来我们来看看实际运用中Function Scope和Block Scope的区别。 代码片段一:
// global scope for(var i = 0; i < 10; i++){ setTimeout(function(){ console.log(i); },100); } 复制代码
代码片段二:
// global scope for(let i = 0; i < 10; i++){ setTimeout(function(){ console.log(i); },100); } 复制代码
运行代码片段一,将会打印出10个10。运行代码片段二,会打印出0、1、2、3、4、5、6、7、8、9。这说明了 var
和 let
是的不同的, var
声明的变量会提升,是作用于Global Scope或Function Scope中的,在代码片段一的 setTimeout
开始执行的时候 for
循环已经运行结束了,这个时候 i
的值是10,所以打印出了10个10。而 let
是作用于Block Scope的,每次循环声明的 i
只在当前的Block Scope中有效,所以每次打印出的 i
都不相同。
#Lexical Scope(词法作用域)
Lexical Scope又称为Static Scope(静态作用域),通俗点说就是你在写代码的时候,某个变量的作用域就已经确定好了,你可以通过直接看代码就能够分析出变量的值。
// global scope function getName() { // function scope var name = '老王' return function () { // function scope console.log(name) } } let showName = getName() function getUser() { // function scope var name = '老李' showName() } console.log(showName()) // 老王 getUser() // 老王 复制代码
当代码执行到 getName
内部的 console.log(name)
时候,不管外部是如何调用的,都是通过向上查找到 var name = '老王'
这条声明赋值语句,从而获取 name
变量的值。所以说在函数还未执行之前,就可以根据Static Scope找到变量对应的值,且这种关系是确定的,不会发生改变,这就是词法作用域。
既然有Static Scope,那么也有Dynamic Scope(动态作用域)。动态作用域意味着在代码执行阶段,变量的取值是会根据作用域的不同而发生变化。
// global scope function user1() { // function scope var name = '老王' getName() } function user2() { // function scope var name = '老李' getName() } function getName() { console.log(name) } user1() // undefined user2() // undefined 复制代码
看以上代码, getName
中并没有声明 name
变量,所以执行结果都为 undefined
。如果JavaScript支持动态作用域,那么 user1()
的执行结果将是 老王
, user2()
的执行结果将是 老李
。事实上,JavaScript并没有Dynamic Scope,它只有Lexical Scope,它们的本质区别是,Lexical Scope是作用于代码编写阶段,关心的是函数在哪声明;而Dynamic Scope是作用于代码执行阶段,关心的是函数在哪被调用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 你了解HTTPS,但你可能不了解X.509
- 你真的了解Mybatis的${}和#{}吗?是否了解应用场景?
- 你所了解的 array_diff_uassoc 真的是你了解的那样吗?
- 图文了解 Kubernetes
- 深入了解 JSONP
- 一文了解 Kubernetes
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Responsive Web Design
Ethan Marcotte / Happy Cog / 2011-6 / USD 18.00
From mobile browsers to netbooks and tablets, users are visiting your sites from an increasing array of devices and browsers. Are your designs ready? Learn how to think beyond the desktop and craft be......一起来看看 《Responsive Web Design》 这本书的介绍吧!