作用域和闭包常见的面试题
作用域
- 变量提升
var scope="global";
function scopeTest(){
console.log(scope);
var scope="local"
}
scopeTest(); //undefined
上面的代码输出是undefined,这是因为局部变量scope变量提升了,等效于下面
var scope="global";
function scopeTest(){
var scope;
console.log(scope);
scope="local"
}
scopeTest(); //undefined
如果在局部作用域中忘记var,那么变量就被声明为全局变量。
2. 没有块级作用域
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0](); // 3
data[1](); // 3
data[2](); // 3
如果要强制返回预期的结果,怎么办???
- 立即执行函数
for (var i = 0; i < 3; i++) {
(function(num) {
setTimeout(function() {
console.log(num);
}, 1000);
})(i);
}
// 0
// 1
// 2
- 返回一个匿名函数赋值
var data = [];
for (var i = 0; i < 3; i++) {
data[i] = (function (num) {
return function(){
console.log(num);
}
})(i);
}
data[0](); // 0
data[1](); // 1
data[2](); // 2
- 使用ES6中的let
var data = [];
for (let i = 0; i < 3; i++) {
data[i] = function () {
console.log(i);
};
}
data[0]();
data[1]();
data[2]();
作用域链
每个函数都有自己的执行上下文环境,当代码在这个环境中执行时,会创建变量对象的作用域链,作用域链是一个对象列表或对象链,它保证了变量对象的有序访问。
作用域链的开始是当前代码执行环境的变量对象,常被称之为“活跃对象”(AO),变量的查找会从第一个链的对象开始,如果对象中包含变量属性,那么就停止查找,如果没有就会继续向上级作用域链查找,直到找到全局对象中
闭包
function createClosure(){
var name = "jack";
return {
setStr:function(){
name = "rose";
},
getStr:function(){
return name + ":hello";
}
}
}
var builder = new createClosure();
builder.setStr();
console.log(builder.getStr()); //rose:hello
上面在函数中返回了两个闭包,这两个闭包都维持着对外部作用域的引用。闭包中会将外部函数的自由对象添加到自己的作用域链中,所以可以通过内部函数访问外部函数的属性,这也是javascript模拟私有变量的一种方式。