大家好,欢迎来到IT知识分享网。
作用域也就是变量对象能被其他对象访问的作用范围
主要包括以下几个概念:
1、全局作用域(Global Scope)与局部作用域(Local Scope)
2、静态(词法)作用域(Lexical Scope)与动态作用域(Dynamic Scope)
3、块级作用域(Block Scope)
4、作用域链(Scope Chain)
现在来具体举例说明下这几个区别:
1、全局作用域,指在全局的范围都能访问到。例如:
let a = 1;
(function foo() {
b = 2;
}())
window.c = 3;
console.log(a, b, c);
其中a,b,c能被全局访问到。a定义在当前全局环境,b没有关键词声明也是全局,c定义在window全局对象下。
2、局部作用域,也就是函数作用域,只有在函数内部才能访问到
var a = 1;
(function foo() {
var a = 2;
var b = 2;
}())
console.log(a, typeof b);
从外部能访问到a,但是外部的a,值为1,而b就是undefined。
局部作用域能访问全局的作用域,子函数能访问父函数的作用域,但父函数不能访问子函数的作用域。如果要想外部访问内部作用域,那就得利用闭包了,相当于利用函数复制了一个封闭的空间出来,供外部调用。
3、词法作用域,也就是作用域在变量定义时决定,范围在所定义的位置,因为JS解析引擎在提取代码进行词法分析生成语法树时就决定了。动态作用域则是程序在运行阶段动态来确定变量的作用域范围,也就是从调用方来查找,而不是定义的环境。JS的call与apply,bind可以动态地改变执行上下文,可以改变变量的执行上下文this,相当于修改了对象作用域的范围,但不是修改作用域。
var a = 1;
var a = 2;
function foo() {
console.log(a);
}
function change() {
var a = 3;
foo();
}
change();
变量a在定义时的作用范围内,所以打印仍是2,foo里面a作用范围在定义时的foo局部作用域内。如果是动态作用域则打印3。一般编程语言都是静态作用域,例如C、Java、Python、JS等,但如shell脚本或velocity模板语言是动态作用域。
4、块级作用域,也就是变量作用范围在一个块内,一般是花括号标识。大多数语言都是块级作用域域。JS直到ES6推出了后,通过let关键字声明的变量作用域在块内。
{
var a = 1;
let b = 1;
}
console.log(a, b);
这里a将打印1,b就会报错,因为变量未定义而调用。
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
这里会打印5次5,因为没有块级作用域,后面的覆盖了前面的,这里一般要用闭包才能打印出1到5来。
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
而如果换成let,则打印1到5,虽然同样是超时打印,这是因为变量的作用域不同。至于let与var的其他不同,以及var带来的变量提升等问题,就不在这里赘述了。
5、作用域链,JS的代码在执行时会针对执行代码块(global, function和eval)创建执行上下文(Execution Context, 包括this, variable object,[[scope]]等),执行上下文中的变量对象会从子函数(是一个执行上下文)逐级往上到父函数(也是一个执行上下文)中查找该变量,一直到全局。函数通过一个[[scope]]属性供其他的其他对象来链接查找。这里注意不要将变量逐级往上查找与变量作用域本身混淆。变量逐级查找是基于作用域链向上查找该变量,而变量作用域是该变量的作用范围,这个范围是声明时所在的位置。同时也不要跟this混淆(this表示对象本身,执行上下文也会创建一个this,用来代表这个执行环境,但并不等于这个上下文),函数的this可以看作是函数的指针,指向函数的调用方,它可以被动态修改。
var a = ‘a’;
function outer() {
var b = ‘b’;
function child(b) {
a = ‘1’;
b = ‘2’;
function inner() {
console.log(‘inner:’, ‘a=’ + a, ‘b=’ + b);
}
inner(b);
}
child(b);
console.log(‘outer:’, ‘a=’ + a, ‘b=’ + b);
}
outer();
输出:
inner: a=1 b=2
outer: a=1 b=b
inner结果原因:a变量在inner里面没有,向上查找child,child里没有,再向上找到outer,依然没有,直到最外层找到a。a在执行child()时被重新重新赋值为1。b是向上在child作用域中找中。
outer结果原因:a在outer作用域下没有,向上才找到a,但a在执行child()时已经被赋值为1了,b因为传递的是值而非引用,因此child()执行,b被修改时并非修改的outer下的b,而是child(b)形参定义的b变量。
以上就把作用域说完了,很简单吧。好了,来一个例子,如果你能说清原理,那就说明理解了。
function foo(b) {
var a = b;
return {
a: a,
change: function(b) {
console.log(a, b, this.a);
a = b + this.a;
},
output: function() {
console.log(a, this.a, b);
}
}
}
var x = foo(1);
console.log(‘change before:’)
console.log(x.a); // 1;
x.output(); // 1, 1, 1
x.a = 3;
x.change(2); // 1, 2, 3
console.log(‘after change:’);
console.log(x.a); // 3
x.output(); // 5, 3, 1
关于这个答案讲解可以放在以后的章节中。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/52257.html