JavaScript中的this,call,apply使用及區(qū)別詳解

1、this

this總是指向一個(gè)對(duì)象,而具體指向哪個(gè)對(duì)象是運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境動(dòng)態(tài)綁定的,而非函數(shù)被聲明時(shí)的環(huán)境。
this的指向可以分為以下四種:

  1. 作為對(duì)象的方法調(diào)用
  2. 作為普通函數(shù)調(diào)用
  3. 構(gòu)造器調(diào)用
  4. Function.prototype.apply和Function.prototype.call調(diào)用
1.作為對(duì)象的方法調(diào)用

當(dāng)函數(shù)作為對(duì)象方法調(diào)用時(shí),this指向該對(duì)象。

/**
 * 1.作為對(duì)象的方法調(diào)用
 *
 * 作為對(duì)象方法調(diào)用時(shí),this指向該對(duì)象。
 */
var obj = {
 a: 1,
 getA: function() {
  console.log(this === obj);   // true 
  console.log(this.a);    // 1
 }
};
 
obj.getA();
2.作為普通函數(shù)調(diào)用

作為普通函數(shù)調(diào)用時(shí),this總是指向全局對(duì)象(瀏覽器中是window)。

/**
 * 2.作為普通函數(shù)調(diào)用
 *
 * 不作為對(duì)象屬性調(diào)用時(shí),this必須指向一個(gè)對(duì)象。那就是全局對(duì)象。
 */
 
window.name = 'globalName';
 
var getName = function() {
 console.log(this.name);
};
 
getName(); // 'globalName'
 
var myObject = {
 name: "ObjectName",
 getName: function() {
  console.log(this.name)
 }
};
 
myObject.getName(); // 'ObjectName'
 
// 這里實(shí)質(zhì)上是把function() {console.log(this.name)}
// 這句話賦值給了theName。thisName在全局對(duì)象中調(diào)用,自然讀取的是全局對(duì)象的name值
var theName = myObject.getName;
3.構(gòu)造器調(diào)用

作為構(gòu)造器調(diào)用時(shí),this指向返回的這個(gè)對(duì)象。

/**
 * 3.作為構(gòu)造器調(diào)用
 * 
 * 作為構(gòu)造器調(diào)用時(shí),this指向返回的這個(gè)對(duì)象。
 */
 
var myClass = function() {
 this.name = "tiany";
};
var obj = new myClass();
console.log(obj.name); // tiany
console.log(obj) // myClass {name: "tiany"}

但是如果構(gòu)造函數(shù)中手動(dòng)指定了return其它對(duì)象,那么this將不起作用。

var myClass = function() {
 this.name = "tiany";
 // 加入return時(shí),則返回的是別的對(duì)象。this不起作用。
 return {
  name:"ReturnOthers"
 }
};
 
var obj = new myClass();
console.log(obj.name); // ReturnOthers

如果return的是別的數(shù)據(jù)類型,則沒有問題。

var myClass = function() {
 this.name = "tiany";
 // 如果return的是別的數(shù)據(jù)類型
 return "othername";   //返回string類型
};
 
var obj = new myClass();
console.log(obj.name); // tiany
4.Call和Apply

Call和Apply的用途一樣。都是用來指定函數(shù)體內(nèi)this的指向。

var obj1= {
      name : "tiany";
      getName : function(){
              return this.name;
      }
};

var obj2= {
      name : "gwt";
};
 
console.log(obj1.getName() ); // tiany
console.log(obj1.getName().call(obj2) ); // gwt
丟失的this
var obj= {
      name : "tiany";
      getName : function(){
              return this.name;
      }
};

console.log(obj.getName() ); // tiany

var getName2 = obj.getName;
console.log(getName2() ); // undefined,此時(shí)this 的指向是全局windows,所以u(píng)ndefined

2、Call和Apply的區(qū)別

Call:第一個(gè)參數(shù)為this的指向,要傳給函數(shù)的參數(shù)得一個(gè)一個(gè)的輸入。
Apply:第一個(gè)參數(shù)為this的指向,第二個(gè)參數(shù)為數(shù)組,一次性把所有參數(shù)傳入。
如果第一個(gè)參數(shù)為null,則this指向調(diào)用的本身。
1.改變this指向
這是call和apply最常用的用途了。用于改變函數(shù)體內(nèi)this的指向。

var name = "GlobalName" 
var func = function() {
 console.log(this.name)
}; 
func(); // "GlobalName" 
var obj = {
 name: "tiany",
 getName: function() {
  console.log(this.name)
 }
}; 
obj.getName.apply(window) // "GlobalName" 將this指向window
func.apply(obj) // "tiany" 將this指向obj

2.借用其它對(duì)象的方法

(function(a, b) {
 console.log(arguments) // 1,2
 // 調(diào)用Array的原型方法
 Array.prototype.push.call(arguments, 3);
 console.log(arguments) // 1,2,3
})(1,2)

函數(shù)具有arguments屬性,而arguments是一個(gè)類數(shù)組。
但是arguments是不能直接調(diào)用數(shù)組的方法的,所以我們要用call或者apply來調(diào)用Array對(duì)象的原型方法。
原理也很容易理解,比如剛才調(diào)用的是push方法,而push方法在谷歌的v8引擎中,源代碼是這樣的:

function ArrayPush() {
 var n = TO_UINT32(this.length); // 被push對(duì)象的長(zhǎng)度
 var m = % _ArgumentsLength(); // push的參數(shù)個(gè)數(shù)
 for (var i = 0; i < m; i++) {
  this[i + n] = % _Arguments(i); // 復(fù)制元素
 }
 this.length = n + m; //修正length屬性
 return this.length;
}

Array.prototype.push

var a = {};
Array.prototype.push.call(a,'first');
alert(a.length); // 1
alert(a[0]);  //first

3、Function.prototype.bind
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Function.prototype.bind = function(context){
          var self = this;  //保存原函數(shù)
          return function(){    //返回一個(gè)新的函數(shù)
                     return self.apply(context,arguments); //執(zhí)行新的函數(shù)時(shí),會(huì)將之前傳入的context當(dāng)做新函數(shù)體的this
          }
}

var obj = {
      name : 'tiany'
};

var func = function(){
       alert(this.name);  // tiany
}.bind(obj);

func();
對(duì)于apply和call兩者在作用上是相同的,但兩者在參數(shù)上有區(qū)別的。對(duì)于第一個(gè)參數(shù)意義都一樣,但對(duì)第二個(gè)參數(shù):apply傳入的是一個(gè)參數(shù)數(shù)組,也就是將多個(gè)參數(shù)組合成為一個(gè)數(shù)組傳入,而call則作為call的參數(shù)傳入(從第二個(gè)參數(shù)開始)。如 func.call(func1,var1,var2,var3)對(duì)應(yīng)的apply寫法為:func.apply(func1,[var1,var2,var3])

相關(guān)參考 《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》
其他一些資料
http://uule.iteye.com/blog/1158829

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在JavaScript編程中,this關(guān)鍵字總是讓初學(xué)者感到迷惑,F(xiàn)unction.prototype.call和...
    白小蟲閱讀 469評(píng)論 0 1
  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡(jiǎn)單...
    舟漁行舟閱讀 8,137評(píng)論 2 17
  • 1. this之謎 在JavaScript中,this是當(dāng)前執(zhí)行函數(shù)的上下文。因?yàn)镴avaScript有4種不同的...
    百里少龍閱讀 1,103評(píng)論 0 3
  • 在經(jīng)過第一次失敗。第一次成功。我懂。哪里有那么好的。那么容易的成功。 想像馬云一樣。就必須先從默默無聞...
    夢(mèng)幽蘭閱讀 189評(píng)論 0 0
  • ——橙子公開課:古典 如今,這是一個(gè)買個(gè)鴨脖都需要掃二維碼關(guān)注公眾賬號(hào)的時(shí)代, 生個(gè)這個(gè)時(shí)代,你應(yīng)該識(shí)趣的知道,想...
    愛君如初閱讀 198評(píng)論 0 1

友情鏈接更多精彩內(nèi)容