关于声明提升、作用域以及闭包的理解

一、声明提升

声明提升有变量声明提升和函数声明提升,只有声明本身会被提升,而赋值或其他运行逻辑会留在原地.

1、变量声明提升

var a = 2; 
//JavaScript 实际上会将其看成两个声明:var a; 和 a = 2;
function foo() {
console.log( a ); // undefined
var a = 2;
}

2、函数声明提升

foo(); // TypeError
bar(); // ReferenceError
var foo = function bar() {
// ...
};
//函数声明会被提升,但是函数表达式却不会被提升
//声明后
var foo;
foo(); // TypeError
bar(); // ReferenceError
foo = function() {
var bar = ...self...
// ...
}

3、函数首先被提升,然后才是变量

foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
//会输出 1 而不是 2 !这个代码片段会被引擎理解为如下形式:
function foo() {
console.log( 1 );
}
foo(); // 1
foo = function() {
console.log( 2 );
};

二、作用域 

函数作用域

var a = 2;
//函数作用域和块作用域 | 27
(function foo(){ // <-- 添加这一行
var a = 3;
console.log( a ); // 3
})(); // <-- 以及这一行
console.log( a ); // 2

三、闭包

1、闭包的理解

function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 2 —— 朋友,这就是闭包的效果。
函数 bar() 的词法作用域能够访问 foo() 的内部作用域。然后我们将 bar() 函数本身当作 一个值类型进行传递。
在这个例子中,我们将 bar 所引用的函数对象本身当作返回值。 在 foo() 执行后,
其返回值(也就是内部的 bar() 函数)赋值给变量 baz 并调用 baz(),
实 际上只是通过不同的标识符引用调用了内部的函数 bar()。
bar() 显然可以被正常执行。但是在这个例子中,它在自己定义的词法作用域以外的地方执行。

2、闭包随处可见
function wait(message) {
setTimeout( function timer() {
console.log( message );
}, 1000 );
}
wait( "Hello, closure!" );

  将一个内部函数(名为 timer)传递给 setTimeout(..)。timer 具有涵盖 wait(..) 作用域 的闭包,因此还保有对变量 message 的引用。

function setupBot(name, selector) {
$( selector ).click( function activator() {
console.log( "Activating: " + name );
} );
}
setupBot( "Closure Bot 1", "#bot_1" );
setupBot( "Closure Bot 2", "#bot_2" );

  在定时器、事件监听器、 Ajax 请求、跨窗口通信、Web Workers 或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包!

四、循环和闭包

  循环时,我们试图假设循环中的每个迭代在运行时都会给自己“捕获”一个 i 的副本。但是 根据作用域的工作原理,实际情况是尽管循环中的五个函数是在各个迭代中分别定义的, 但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个 i。我们需要更多的闭包作用域,特别是在循环的过程中每个迭代都需要一个闭包作用域。

for (var i=1; i<=5; i++) {
(function(j) {
setTimeout( function timer() {
console.log( j );
}, j*1000 );
})( i );
}
//在迭代内使用 IIFE 会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的
作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。
for (let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
//很酷是吧?块作用域和闭包联手便可天下无敌。

  

 

猜你喜欢

转载自www.cnblogs.com/blancher/p/10515308.html
今日推荐