Products
GG网络技术分享 2025-03-18 16:14 2
任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。本文的主要内容有:
变量的作用域名
函数作用域和声明提前
作为属性的变量的作用域
作用域链
一个变量的作用域(scope) 是程序源代码中定义这个变量的区域。 全局变量拥有全局作用域, 在JavaScript代码中的任何地方都是有定义的。 然而在函数内声明的变量只在函数体内有定义。 它们是局部变量, 作用域是局部性的。 函数参数也是局部变量, 它们只在函数体内有定义。
在函数体内, 局部变量的优先级高于同名的全局变量。 如果在函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名, 那么全局变量就被局部变量所遮盖。
尽管在全局作用域编写代码时可以不写v a r语句, 但声明局部变量时则必须使用var语句。 思考一下如果不这样做会怎样:
函数定义是可以嵌套的。 由于每个函数都有它自己的作用域, 因此会出现几个局部作用域嵌套的情况, 例如:
在一些类似C语言的编程语言中, 花括号内的每一段代码都具有各自的作用域, 而且变量在声明它们的代码段之外是不可见的, 我们称为块级作用域(blockscope) ,而JavaScript中没有块级作用域。 JavaScript取而代之地使用了函数作用域(functionscope) : 变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
在如下所示的代码中, 在不同位置定义了变量i、 j和k, 它们都在同一个作用域内——这三个变量在函数体内均是有定义的。
JavaScript的函数作用域是指在函数内声明的所有变量在函数体内始终是可见的。 有意思的是, 这意味着变量在声明之前甚至已经可用。 JavaScript的这个特性被非正式地称为声明提前(hoisting) , 即JavaScript函数里声明的所有变量(但不涉及赋值) 都被“提前” 至函数体的顶部译注10, 看一下如下代码:
你可能会误以为函数中的第一行会输出“global” , 因为代码还没有执行到var语句声明局部变量的地方。 其实不然, 由于函数作用域的特性, 局部变量在整个函数体始终是有定义的, 也就是说, 在函数体内局部变量遮盖了同名全局变量。 尽管如此, 只有在程序执行到v a r语句的时候, 局部变量才会被真正赋值。 因此, 上述过程等价于: 将函数内的变量声明“提前” 至函数体顶部, 同时变量初始化留在原来的位置:
在具有块级作用域的编程语言中, 在狭小的作用域里让变量声明和使用变量的代码尽可能靠近彼此, 通常来讲, 这是一个非常不错的编程习惯。 由于JavaScript没有块级作用域, 因此一些程序员特意将变量声明放在函数体顶部, 而不是将声明靠近放在使用变量之处。 这种做法使得他们的源代码非常清晰地反映了真实的变量作用域。
当声明一个JavaScript全局变量时, 实际上是定义了全局对象的一个属性 。当使用var声明一个变量时, 创建的这个属性是不可配置的 , 也就是说这个变量无法通过delete运算符删除。 可能你已经注意到了, 如果你没有使用严格模式并给一个未声明的变量赋值的话, JavaScript会自动创建一个全局变量。 以这种方式创建的变量是全局对象的正常的可配值属性, 并可以删除它们:
JavaScript全局变量是全局对象的属性, 这是在ECMAScript规范中强制规定的。 对于局部变量则没有如此规定, 但我们可以想象得到, 局部变量当做跟函数调用相关的某个对象的属性。 ECMAScript 3规范称该对象为“调用对象” (call object), ECMAScript 5规范称为“声明上下文对象” (declarative environment record) 。 JavaScript可以允许使用this关键字来引用全局对象, 却没有方法可以引用局部变量中存放的对象。 这种存放局部变量的对象的特有性质, 是一种对我们不可见的内部实现。 然而, 这些局部变量对象存在的观念是非常重要的。
JavaScript是基于词法作用域的语言: 通过阅读包含变量定义在内的数行源码就能知道变量的作用域。 全局变量在程序中始终都是有定义的。 局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的。
如果将一个局部变量看做是自定义实现的对象的属性的话, 那么可以换个角度来解读变量作用域。 每一段JavaScript代码(全局代码或函数) 都有一个与之关联的作用域链(scope chain) 。 这个作用域链是一个对象列表或者链表, 这组对象定义了这段代码“作用域中” 的变量。 当JavaScript需要查找变量x的值的时候(这个过程称做“变量解析” (variable resolution) ) , 它会从链中的第一个对象开始查找, 如果这个对象有一个名为x的属性, 则会直接使用这个属性的值, 如果第一个对象中不存在名为x的属性,JavaScript会继续查找链上的下一个对象。 如果第二个对象依然没有名为x的属性, 则会继续查找下一个对象, 以此类推。 如果作用域链上没有任何一个对象含有属性x, 那么就认为这段代码的作用域链上不存在x,并最终抛出一个引用错误(ReferenceError)异常。
在JavaScript的最顶层代码中(也就是不包含在任何函数定义内的代码) , 作用域链由一个全局对象组成。 在不包含嵌套的函数体内, 作用域链上有两个对象, 第一个是定义函数参数和局部变量的对象, 第二个是全局对象。 在一个嵌套的函数体内, 作用域链上至少有三个对象。 理解对象链的创建规则是非常重要的。 当定义一个函数时, 它实际上保存一个作用域链。 当调用这个函数时, 它创建一个新的对象来存储它的局部变量, 并将这个对象添加至保存的那个作用域链上, 同时创建一个新的更长的表示函数调用作用域的“链” 。 对于嵌套函数来讲, 事情变得更加有趣, 每次调用外部函数时, 内部函数又会重新定义一遍。 因为每次调用外部函数的时候, 作用域链都是不同的。 内部函数在每次定义的时候都有微妙的差别——在每次调用外部函数时, 内部函数的代码都是相同的, 而且关联这段代码的作用域链也不相同。
作用域链的概念对于理解w i t h语句是非常有帮助的, 同样对理解闭包 的概念也至关重要。
作用域是可访问变量的集合。
JavaScript 作用域
在 JavaScript 中, 对象和函数同样也是变量。
在 JavaScript 中, 作用域为可访问变量,对象,函数的集合。
JavaScript 函数作用域: 作用域在函数内修改。
JavaScript 局部作用域
变量在函数内声明,变量为局部作用域。
局部变量:只能在函数内部访问。
// 此处不能调用 carName 变量 function myFunction() { var carName = \"Volvo\"; // 函数内可调用 carName 变量 } |
因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。
局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。
JavaScript 全局变量
变量在函数外定义,即为全局变量。
全局变量有 全局作用域: 网页中所有脚本和函数均可使用。
var carName = \" Volvo\"; // 此处可调用 carName 变量 function myFunction() { // 函数内可调用 carName 变量 } |
如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量。
以下实例中 carName 在函数内,但是为全局变量。
// 此处可调用 carName 变量 function myFunction() { carName = \"Volvo\"; // 此处可调用 carName 变量 } |
JavaScript 变量生命周期
JavaScript 变量生命周期在它声明时初始化。
局部变量在函数执行完毕后销毁。
全局变量在页面关闭后销毁。
函数参数
函数参数只在函数内起作用,是局部变量。
HTML 中的全局变量
在 HTML 中, 全局变量是 window 对象: 所有数据变量都属于 window 对象。
//此处可使用 window.carName function myFunction() { carName = \"Volvo\"; } |
你知道吗?
你的全局变量,或者函数,可以覆盖 window 对象的变量或者函数。
局部变量,包括 window 对象可以覆盖全局变量和函数。
补充
ES6 中的 let 关键字
let 允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与var关键字不同的是,它声明的变量只能是全局或者整个函数块的。
let 语法:
let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];
let 声明的变量只在其声明的块或子块中可用,这一点,与 var 相似。二者之间最主要的区别在于 var 声明的变量的作用域是整个封闭函数。
let 和 var 的区别代码实例:
function varTest() { var x = 1; if (true) { var x = 2; // 同样的变量! console.log(x); // 2 } console.log(x); // 2 } function letTest() { let x = 1; if (true) { let x = 2; // 不同的变量 console.log(x); // 2 } console.log(x); // 1 } |
以上就是详解JavaScript之作用域的详细内容,更多请关注网站的其它相关文章!
Demand feedback