深刻學習js系列是本身階段性成長的見證,但願經過文章的形式更加嚴謹、客觀地梳理js的相關知識,也但願可以幫助更多的前端開發的朋友解決問題,期待咱們的共同進步。javascript
若是以爲本系列不錯,歡迎點贊、評論、轉發,您的支持就是我堅持的最大動力。html
this關鍵字是JavaScript中最複雜的機制之一,它是一個很特別的關鍵字,被自動定義在全部函數的做用域 中。前端
跟別的語言截然不同的是:js的this老是指向一個對象,而具體指向哪一個對象是在運行時基於函數的執行環境動態綁定的,而非函數被聲明時候的環境。java
若是學習this的代價很大,可是對於咱們平時工做並不大,咱們幹嗎要付出這麼大的代價學習呢?的確,在介紹怎麼作以前咱們須要先明白爲何。chrome
除去不經常使用的with和eval的狀況,具體到實際應用中,this的指向大體能夠分爲如下四種:數組
- 1.做爲對象的方法調用。
- 2.做爲普通函數調用。
- 3.構造器調用。
- 4.Function.prototype.call 或者 Function.prototype.apply 下面分爲這四種狀況分別進行調用:
當函數做爲對象的方法被調用的時候,this指向該對象:瀏覽器
var obj = {
a: 1,
getA: function () {
console.log(this === obj); // true
console.log(this.a); // 1;
}
}
obj.getA();
複製代碼
當函數不做爲對象的屬性被調用時候,也就是咱們所說的普通函數方式,此時的this老是指向全局的對象。在瀏覽器的js裏面,這個全局對象是window對象。微信
// 建立全局的name對象 掛載在window上面
window.name = "globalName";
var getName = function () {
return this.name;
}
console.log(getName()); // 輸出的是 globalName
複製代碼
或者閉包
window.name = "globalName";
var myObject = {
name: "louis",
getName: function () {
return this.name;
}
}
var getName = myObject.getName;
console.log(getName()); // "globalName"
複製代碼
有時候咱們會遇到一些困擾,好比在事件節點的div函數內部,有一個局部的callback方法,callback被做爲普通的函數被調用時,callback內部的this指向了window,但咱們每每想讓它的指向div節點.app
<div id = "div1">我是一個div</div>
複製代碼
window.id = "window";
document.getElementById('div1').onclick = function () {
alert(this.id); // 輸出:'div1'
var callback = function () {
alert(this.id); // 輸出:'window'
}
callback();
};
複製代碼
此時有一種簡單的解決方案,能夠用一個變量保存div節點的引用:
document.getElementById('div1').onclick = function () {
var that = this; // 保存div的引用
var callback = function () {
alert(that.id); // 輸出:'div1'
}
callback();
}
複製代碼
在ECMAScript 2015 中的嚴格模式下,這種狀況下的this指向已經被規定爲不會指向全局對象,而是undefined:
function func() {
"use strict"
alert(this); // undefined
}
func();
複製代碼
js中沒有類,可是能夠從構造器中建立對象,同時也提供了new運算符,使得構造器看起來像是一個類,
除了宿主提供的一些內置函數,大部分js函數均可以當成構造器使用,構造器的外表看起來和普通的函數沒有什麼區別,他們的區別在於調用方式,當使用new運算符調用函數的時候,該函數老是返回一個對象,一般狀況下,構造器裏面的this就是指向返回的這個對象。
var MyClass = function () {
this.name = "louis";
}
var obj = new MyClass();
console.log(obj.name);
複製代碼
可是new調用構造器時候,還要注意一個問題,若是構造器顯式的返回了一個object對象那麼這次運算結果最終會返回這個對象,而不是咱們以前期待的this:
var MyClass = function () {
this.name = 'sven';
return { // 顯式地返回一個對象
name: 'anne'
}
};
var obj = new MyClass();
alert(obj.name); // 輸出:anne」
複製代碼
若是構造器不顯式的返回任何數據,或是返回一個非對象類型的數據,就不會形成上述問題。
var MyClass = function () {
this.name = 'sven'
return 'anne'; // 返回string類型
};
var obj = new MyClass();
alert(obj.name); // 輸出:sven」
複製代碼
跟普通函數調用相比,用 Function.prototype.call 或者 Function.prototype.apply能夠動態的改變傳入函數的this:
var obj1 = {
name: "louis",
getName: function () {
return this.name;
}
}
var obj2 = {
name: "kerry";
}
console.log(obj1.getName()); // 輸出: louis
console.log(obj1.getName.call(obj2)); // 輸出: kerry
複製代碼
call 和 apply 方法可以很好的體現 js的函數式語言特性 在js中幾乎每一次編寫函數式語言風格的代碼都離不開call和apply
下面看一個常常遇到的問題:
var obj = {
myName: "louis",
getName: function () {
return this.name;
}
}
console.log(obj.getName()); // louis;
var getName2 = obj.getName;
console.log(getName2()) // undefined
複製代碼
當調用obj.getName時,getName方法是做爲obj對象的屬性被調用的,根據上文提到的規律,此時的this指向obj對象,因此obj.getName()輸出'louis'。
當用另一個變量getName2來引用obj.getName,而且調用getName2時, 此時是普通函數調用方式,this是指向全局window的,window上面並無掛載任何屬性因此程序的執行結果是undefined。
再看另外一個例子,document.getElementById這個方法名實在有點過長,咱們大概嘗試過用一個短的函數來代替它,如同prototype.js等一些框架所作過的事情:
var getId = function (id) {
return document.getElementById(id);
};
getId('div1');
複製代碼
咱們也許思考過爲何不能用下面這種更簡單的方式
var getId = document.getElementById;
getId( 'div1' );
複製代碼
如今不妨花1分鐘時間,讓這段代碼在瀏覽器中運行一次
<div id="div1">我是一個div</div>
複製代碼
var getId = document.getElementById;
getId( 'div1' );
複製代碼
在chrome friefox IE10 中執行事後就會發現,這段代碼拋出一個異常,這是由於不少引擎的document.getElementById 方法的內部實現中須要用到this,這個this原本被指望指向document,當getElementById方法做爲document對象的屬性被調用時,方法內部的this確實是指向document的。
可是當getId來引用document,getElementById以後,再調用getId,此時就成了普通的函數調用了,函數內部的this指向了window,而不是原來的document。
咱們能夠嘗試利用apply把document當作this傳遞給getId函數,修正 this指向問題。
document.getElementById = (function(func){
return function(){
return func.apply(document,arguments);
}
})(document.getElementById);
var getId = document.getElementById;
var div = getId('div1');
alert(div.id);
複製代碼
歡迎添加個人我的微信討論技術和個體成長。