目录
1.背景介绍
2.知识剖析
3.常见问题
4.解决方案
5.编码实战
6.扩展思考
7.参考文献
8.更多讨论
JS中声明的变量跟函数有它自己的可访问范围,超出这个范围就访问不到这个变量\函数。这个可访问范围就是今天要说的作用域。
作用域控制着变量跟函数的可见性和生命周期。
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说一下几种情形拥有全局作用域:
(1)所有末定义直接赋值的变量自动声明为拥有全局作用域,例如:
function person(){
var Name="老威";
nickName="掏粪男孩";
}
person();
console.log(nickName);
console.log(Name);
变量nickName拥有全局作用域,而Name在函数外部无法访问到。
(2)所有window对象的属性拥有全局作用域
一般情况下,window对象的内置属性都都拥有全局作用域,例如window.name、window.top等等。
var site = 'baidu.com';
function getSite() {
alert(this.site);
}
alert(window.site); // 'baidu.com'
getSite(); // 'baidu.com'
window.getSite(); // 'baidu.com'
在上面示例中,site变量和getSite()方法没有指定上级对象,所在二者会被添加到window全局对象,所以直接访问二者与通过window访问本质相同(如,直接访问getSite()与使用window.getSite()访问一样)。
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域成为函数作用域
例如下列代码中的nickName和函数inner都只拥有局部作用域:
function person(){
var nickName="掏粪男孩";
function inner(){
alert(nickName);
}
inner();
}
alert(nickName);
inner();
说完了作用域我们就可以接着来聊聊作用域链(Scope Chain)这个概念了.作用域链就是由多个作用域组成的 在JS中,函数的可以允许嵌套的。即,在一个函数的内部声明另一个函数.类似这样:
function A(){
var a=1;
function B(){ //在A函数内部,声明了函数B,这就是所谓的函数嵌套。
var b=2;
}
}
对于A来说,A函数在执行的时候,会创建其A函数的作用域, 那么函数B在创建的时候,会引用A的作用域,类似下面这样
函数B在执行的时候,其作用域类似于下面这样:
从上面的两幅图中可以看出,函数B在执行的时候,是会引用函数A的作用域的。所以,像这种函数作用域的嵌套就组成了所谓的函数作用域链。当在自身作用域内找不到该变量的时候,会沿着作用域链逐步向上查找,若在全局作用域内部仍找不到该变量,则会抛出异常。
其实作用域链就是JS引擎查询数据的一个链表,后定义的覆盖先定义的,查询不到定义的数据就往深一层查询,一直到全局作用域为止 但是越往内层延伸,读写速度就会越慢,查找全局变量是最慢的。所以,在编写代码的时候应尽量少使用全局变量,尽可能使用局部变量。 如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用。例如下面的代码:
function changeColor(){
document.getElementById("a").onclick=function(){
document.getElementById("b").style.backgroundColor="red";
document.getElementById("a").style.backgroundColor="red"; };
}
这个函数引用了两次全局变量document,查找该变量必须遍历整个作用域链,直到最后在全局对象中才能找到。这段代码可以重写如下:
function changeColor(){
var a=document.getElementById("a");
var b=document.getElementById("b")
a.onclick=function(){
a.style.backgroundColor="red";
b.style.backgroundColor="red";
}; }
这段代码比较简单,但是如果程序中有大量的全局变量被从新反复访问,那么重写后的代码性能会有显著改善。
当 JavaScript 代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。
虽然在定义上跟作用域还是有些差别的,执行上下文其实可以理解为作用域。
在JavaScript解释器内部,每次调用执行上下文,分为两个阶段:
当进入执行上下文时,会创建一个变量对象,所有在执行上下文里创建的变量、函数和形参等都会挂在这个变量对象上面(变量\函数提升)。
举个例子
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
在进入执行上下文后,这时候的 AO 是:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: reference to function c(){},
d: undefined
}
在代码执行阶段,会顺序执行代码,根据代码,修改变量对象的值
还是上面的例子,当代码执行完后,这时候的 AO 是:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 3,
c: reference to function c(){},
d: reference to FunctionExpression "d"
}
参考:理解 JavaScript 作用域和作用域链
JavaScript深入之作用域链
了解JavaScript的执行上下文
感谢大家观看
BY : 钟楚炯