一席纽、方法
在一個對象中綁定函數(shù),稱為這個對象的方法撞蚕。
在JavaScript中润梯,對象的定義是這樣的:
var xiaoming = {
name: '小明',
birth: 1990
};
但是陋葡,如果我們給xiaoming綁定一個函數(shù)侣颂,就可以做更多的事情譬涡。比如矾睦,寫個age()方法信柿,返回xiaoming的年齡:
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年調用是27,明年調用就變成28了
綁定到對象上的函數(shù)稱為方法囤捻,和普通函數(shù)也沒啥區(qū)別评抚,但是它在內部使用了一個this關鍵字熟空,這個東東是什么谦秧?
★在一個方法內部竟纳,this是一個特殊變量,它始終指向當前對象疚鲤,也就是xiaoming這個變量锥累。所以,this.birth可以拿到xiaoming的birth屬性集歇。
讓我們拆開寫:
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 27, 正常結果
getAge(); // NaN
【注意】M奥浴!诲宇!單獨調用函數(shù)getAge()怎么返回了NaN际歼?請注意,我們已經進入到了JavaScript的一個大坑里姑蓝。
JavaScript的函數(shù)內部如果調用了this鹅心,那么這個this到底指向誰?
答案是纺荧,視情況而定巴帮!
如果以對象的方法形式調用溯泣,比如xiaoming.age(),該函數(shù)的this指向被調用的對象榕茧,也就是xiaoming垃沦,這是符合我們預期的。
如果單獨調用函數(shù)用押,比如getAge()肢簿,此時,該函數(shù)的this指向全局對象蜻拨,也就是window池充。
坑爹啊缎讼!
更坑爹的是收夸,如果這么寫:
var fn = xiaoming.age; // 先拿到xiaoming的age函數(shù)
fn(); // NaN
也是不行的!
★★★要保證this指向正確血崭,必須用obj.xxx()的形式調用卧惜!
由于這是一個巨大的設計錯誤,要想糾正可沒那么簡單夹纫。★ECMA決定咽瓷,在strict模式下讓函數(shù)的this指向undefined,因此舰讹,在strict模式下茅姜,你會得到一個錯誤:
'use strict';
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
var fn = xiaoming.age;
fn(); // Uncaught TypeError: Cannot read property 'birth' of undefined
這個決定只是讓錯誤及時暴露出來,并沒有解決this應該指向的正確位置月匣。
有些時候钻洒,喜歡重構的你把方法重構了一下:
'use strict';
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - this.birth;
}
return getAgeFromBirth();
}
};
xiaoming.age(); // Uncaught TypeError: Cannot read property 'birth' of undefined
結果又報錯了!原因是this指針只在age方法的函數(shù)內指向xiaoming锄开,在函數(shù)內部定義的函數(shù)素标,this又指向undefined了!(在非strict模式下院刁,它重新指向全局對象windowE锤啤)
修復的辦法
我們★★★用一個that變量首先捕獲this:
'use strict';
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法內部一開始就捕獲this
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
};
xiaoming.age(); // 27
用var that = this;粪狼,你就可以放心地在方法內部定義其他函數(shù)退腥,而不是把所有語句都堆到一個方法中。
二再榄、apply
雖然在一個獨立的函數(shù)調用中狡刘,根據是否是strict模式,this指向undefined或window困鸥,不過嗅蔬,我們還是可以控制this的指向的剑按!
要指定函數(shù)的this指向哪個對象,可以用★函數(shù)本身的apply方法.
它接收兩個參數(shù)澜术,★第一個參數(shù)就是需要綁定的this變量艺蝴,第二個參數(shù)是Array,表示函數(shù)本身的參數(shù)鸟废。
用apply修復getAge()調用:
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}
var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};
xiaoming.age(); // 27
getAge.apply(xiaoming, []); // 27, this指向xiaoming, 參數(shù)為空
三猜敢、call
另一個與apply()類似的方法是★call(),唯一區(qū)別是:
** apply()把參數(shù)打包成Array再傳入盒延;**
** call()把參數(shù)按順序傳入缩擂。**
比如調用Math.max(3, 5, 4),分別用apply()和call()實現(xiàn)如下:
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5
對普通函數(shù)調用添寺,我們通常把this綁定為null胯盯。
四、裝飾器
利用apply()计露,我們還可以動態(tài)改變函數(shù)的行為博脑。
JavaScript的所有對象都是動態(tài)的,即使內置的函數(shù)薄坏,我們也可以重新指向新的函數(shù)趋厉。
現(xiàn)在假定我們想統(tǒng)計一下代碼一共調用了多少次parseInt(),可以把所有的調用都找出來胶坠,然后手動加上count += 1君账,不過這樣做太傻了。最佳方案是用我們自己的函數(shù)替換掉默認的parseInt():
var count = 0;
var oldParseInt = parseInt; // 保存原函數(shù)
window.parseInt = function () {
count += 1;
return oldParseInt.apply(null, arguments); // 調用原函數(shù)
};
// 測試:
parseInt('10');
parseInt('20');
parseInt('30');
count; // 3