一、作用域
var可以重复声明,重复声明时实际是跳过声明处理,继续执行赋值操作。
宽松模式下,a=2
如果找不到a
的声明,会在全局声明一个a
;严格模式下严格模式禁止自动或隐式地创建全局变量。
function foo(a) {
console.log(a + b);
b = a;
}
foo(2); // ReferenceError: b is not defined,直接使用b时作用域内找不到b,所以报引用错误
function foo(a) {
console.log(a + b);
let b = a;
}
foo(2); // ReferenceError: Cannot access 'b' before initialization, let声明不会变量提升
function foo(a) {
console.log(a + b);
var b = a;
}
foo(2); // NaN, var声明存在变量提升,用b时未赋值但已声明,不会报错,返回undefined
function foo(a) {
var b = a;
}
foo(2);
console.log(b); // ReferenceError: b is not defined, var在foo函数内部定义的变量,只在foo内部能访问
function foo(a) {
b = a;
}
foo(2);
console.log(b); // 2,b是定义在全局的,外部能够访问到
function foo(a) {
b = a;
}
var b = 3;
foo(2);
console.log(b); // 2,foo内部修改了外部的b
function foo() {
function bar(a) {
i = 3;
console.log(a + i);
}
for (var i=0; i<10; i++) {};
bar(2); // 5,这里的i是foo内部for循环定义的i
}
foo();
console.log(i); // ReferenceError: i is not defined,外部访问不到内部的i
function foo() {
function bar(a) {
i = 3;
console.log(a + i);
}
bar(2);
}
foo(); // 5
console.log(i); //3,i在全局定义
(function foo(){
var a = 3;
console.log(a);
})();
foo; //ReferenceError: foo is not defined,上面为函数表达式,外部访问不到foo
a=1;
var b=2;
let c=3;
console.log(window.a,window.b,window.c);; // 1,2,undefined,let声明的变量不会挂到window上
function process(data) {
// 在这里做点有趣的事情
}
// 在这个块中定义的内容完事可以销毁!因为someReallyBigData只在块作用域内有效
{
let someReallyBigData = { .. };
process(someReallyBigData);
}
var btn = document.getElementById("my button");
btn.addEventListener("click", function click(evt){
console.log("button clicked"); // click形成了一个覆盖整个作用域的闭包
}, /*capturingPhase=*/false );
foo();
function foo() {
console.log(a); // undefined
var a = 2;
}
console.log(a); // undefined,a在foo函数作用域内
foo(); // TypeError,能找到foo,因为变量提升,但是是undefined,直接调用报类型错误
bar(); // ReferenceError,表达式后面的具名函数不能被提升
var foo = function bar() {
// ...
};
foo(); // 1
var foo; // 函数声明优先级高于变量,重复声明被忽略
function foo() {
console.log(1);
}
foo = function() {
console.log(2);
};
foo(); // 3 可重复赋值声明,后面会覆盖前面
function foo() {
console.log(1);
}
var foo = function() {
console.log(2);
};
function foo() {
console.log(3);
}
二、闭包
无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。
function foo() {
var a = 2;
function bar() {
console.log(a);
}
return bar;
}
var baz = foo(); // 因为foo内部的bar被传出,bar引用了变量a,引擎不知道什么时候会用到a,因此不会销毁foo的内部作用域
baz(); // 2 bar()依然持有对foo内部作用域的引用,而这个引用就叫作闭包。
for (var i=1; i<=5; i++) {
setTimeout(function timer() {
console.log(i);
}, i*1000 );
} // 以每秒一次的频率输出五次6,放入event loop时i*1000已经被转换为时间,但最后打印的i用的是全局定义的
for (var i=1; i<=5; i++) {
(function() {
setTimeout(function timer() {
console.log(i);
}, i*1000 );
})();
} // 效果同上,空IIFE里面找不到i,同样是去外部找
for (var i=1; i<=5; i++) {
(function() {
var j = i;
setTimeout(function timer() {
console.log(j);
}, j*1000 );
})();
} // 每隔一秒依次输出递增的i
for (var i=1; i<=5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j*1000 );
})(i);
} // 结果同上,正常执行
for (var i=1; i<=5; i++) {
let j = i; // 是的,闭包的块作用域!
setTimeout( function timer() {
console.log(j);
}, j*1000 );
} // 每次循环创建一个单独的块,存放循环体的内容,因此在块内部j是唯一的
for (let i=1; i<=5; i++) {
setTimeout(function timer() {
console.log(i);
}, i*1000 );
} // 效果同上,正常执行
标签:闭包,function,console,log,作用域,var,foo
From: https://www.cnblogs.com/lhjc/p/17813700.html