個人博客倉庫html
this, call, apply 和 bind 以及箭頭函數git
用來放一些我看的書籍和學習的記錄,歡迎點贊和討論。github
在瞭解閉包以前,有一些概念必須瞭解,這些是造成閉包的先決條件。promise
var age = 10;
function Tom(){
var name = 'Tom';
function say(){
console.log('My name is '+name);
console.log('My age is '+age);
console.log(person);
}
say();
}
//Tom();
function Person(){
var person = 'person';
Tom();
}
Person();
// My name is Tom
// My age is 10
// error: person is not defined
複製代碼
var age = 10;
function Person(){
var person = 'person';
function Tom(){
var name = 'Tom';
function say(){
console.log('My name is '+name);
console.log('My age is '+age);
console.log(person);
}
say();
}
Tom();
}
Person();
// My name is Tom
// My age is 10
// person
複製代碼
上面 Tom() 建立了一個局部變量 name 和一個名爲 say() 的函數, say() 是定義在 Tom() 裏的內部函數,僅在該函數體內可用。say() 內沒有本身的局部變量,然而它能夠訪問到外部函數的變量,因此 say() 可使用父函數 Tom() 中聲明的變量 name 。可是,若是有同名變量 name 在 say() 中被定義,則會使用 say() 中定義的 name 。bash
看到這裏你可能就有疑問了,詞法做用域究竟是什麼呢?閉包
它就是指在函數嵌套中,變量解析的一種規則,根據變量在代碼中聲明的位置,嵌套的函數在查找變量時,若是本身沒有聲明,那麼能夠訪問外部的變量。app
上面兩個例子還涉及到 this 的問題。異步
定義:函數 A 有一個函數 B ,函數 B 引用了函數 A 的變量, 函數 B 就叫作閉包。函數
下面是偏函數的示例,柯里化和偏函數稍微有些區別,放到單獨一個模塊來講。性能
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
複製代碼
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
複製代碼
這是在循環中建立閉包的常見錯誤,這段代碼會輸出五個6,緣由是給 setTimeout 的函數是閉包,循環六次造成六個閉包,這六個閉包在循環環境中被建立,共享了一個詞法做用域,這個做用域存在一個變量 i ,異步執行的時候循環早已經結束,此時 i = 6 因此輸出的都是 6 。
方法1: let ,並且這樣不會產生閉包
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
複製代碼
方法2:匿名函數
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
複製代碼
方法三: 使用閉包
function callback(j){
return function () {
console.log(j)
}
}
for (var i = 1; i <= 5; i++) {
setTimeout(callback(i), i * 1000)
}
複製代碼
方法四:setTimeout 的第三個參數
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
},i * 1000,i)
}
複製代碼
閉包在處理速度和內存消耗方面都有比較大的開銷。
例如:在建立新的對象或者類時,方法一般應該關聯於對象的原型,而不是定義到對象的構造器中。緣由是這將致使每次構造器被調用時,方法都會被從新賦值一次(也就是,每一個對象的建立)。
function Persion(name,age){
this.name = name.toString();
this.age = Number(age) || 0;
this.getName = function(){
console.log(this.name);
};
this.getAge = function(){
console.log(this.age);
}
}
複製代碼
應該這樣作
function Persion(name,age){
this.name = name.toString();
this.age = Number(age) || 0;
}
Persion.prototype.getName = function(){
console.log(this.name);
};
Persion.prototype.getAge = function(){
console.log(this.age);
};
複製代碼
如今統一將 callback 函數換成了 promise 。