JavaScript設計模式與開發實踐 | this、call和apply

來源:互聯網
上載者:User

標籤:

原文地址:http://segmentfault.com/a/1190000003959359

 

this

JavaScript的this總是指向一個對象,至於指向哪個對象,是在運行時基於函數的執行環境的動態綁定的,而非函數被聲明時的環境。

this的指向

this的指向大致可以分為以下4類:

  • 作為對象的方法調用

  • 作為普通函數調用

  • 構造器調用

  • Function.prototype.callFunction.prototype.apply調用

1.作為對象的方法調用

當函數作為對象的方法被調用時,this指向該對象:

// 聲明obj對象var obj = {    a: ‘a屬性的值‘,    // a 屬性    getA: function(){  // getA()方法        console.log(this === obj);  // 輸出:true        console.log(this.a);  // 輸出: a屬性的值    }};obj.getA();

2.作為普通函數調用

當函數不作為對象的屬性被調用時,也就是以普通函數方式,this指向全域對象。在瀏覽器的JavaScript裡,全域對象是window對象。

window.name = ‘globalName‘;  // 聲明全域對象的name屬性var getName = function(){  // 定義getName()函數    return this.name;};// 調用函數console.log(getName());  //輸出: globalName
window.name = ‘globalName‘;  // 聲明全域對象的name屬性var myObject = {  // 聲明myObject對象    name: ‘objectName‘;    getName: function(){  // 定義getName()方法        return this.name;    }}var getName = myObject.getName;  // 將getName()方法賦給變數getNameconsole.log(getName());  // 輸出: globalName

3.構造器調用

JavaScript沒有類,但可以從構造器中建立對象,也提供了new運算子用於調用構造器。

大部分JavaScript函數都可以當作構造器使用。構造器的外表跟普通函數一樣,他們的區別在於被調用的方式。即,使用new運算子建立對象時,就是將函數當作構造器調用。當用new運算子調用函數時,該函數總會返回一個對象,此時,構造器裡的this指向返回的這個對象。

var myClass = function(){    this.name = ‘className‘;};var obj = new myClass();console.log(obj.name);  // 輸出:seven

但,如果構造器顯式地返回了一個object類型的對象,那麼此函數將返回這個object類型的對象,而不是函數本身所定義的對象,例如:

var myClass = function(){    this.name = ‘className‘;    return {  //顯式地返回一個對象        name: ‘anne‘    }};var obj = new myClass();console.log(obj.name);  //  輸出:anne

而,如果構造器不顯式地返回任何資料,或返回一個非物件類型的資料,就不會造成上述情形。

var myClass = function(){    this.name = ‘className‘;    return ‘anne‘;  // 返回string類型};var obj = new myClass();console.log(obj.name);  //  輸出:className

4.Function.prototype.call 或 Function.prototype.apply調用

跟普通函數調用相比,用 Function.prototype.call 或 Function.prototype.apply 可以動態地改變傳入函數的this。

var A = {    name: ‘ObjectA‘,    getName: function(){        return this.name;    }};var B = {    name: ‘ObjectB‘};console.log(A.getName()); // 作為對象的方法調用,輸出:ObjectAconsole.log(A.getName.call(B)); // 輸出:ObjectB
丟失的this

我們經常會因為this的指向與我們的期待不同,而出現undefined的情況,例如:

var obj = {    name: ‘objName‘;    getName: function(){        return this.name;    }};// 作為對象的方法調用,指向obj對象console.log(obj.getName());   // 輸出:objName// 作為普通函數調用,指向全域對象window,name屬性尚未定義var getName2 = obj.getName;console.log(getName2());  // 輸出:Lundefined
call 和 apply

ECAMScript3給Function的原型定義了兩個方法,分別是Function.prototype.call 或 Function.prototype.apply。在一些函數式風格的代碼編寫中,call和apply方法尤為有用。

call和apply的區別

Function.prototype.call 或 Function.prototype.apply的作用一模一樣,區別僅在於傳入參數形式的不同。

apply接受兩個參數,第一個參數制定了函數體內this對象的指向,第二個函數為一個帶下標的集合,這個集合可以是數組,也可以是類數組。apply方法把這個集合中的元素作為參數傳遞給被調用的函數。

var func = function(a, b, c){  console.log([a, b, c]);  // 輸出:[1,2,3]};func.apply(null, [1, 2, 3]);

call傳入的參數數量不固定,第一個參數也是代表了函數體內的this指向,從第二個參數開始往後,每個參數依次被傳入函數:

var func = function(a, b, c){  console.log([a, b, c]);  // 輸出:[1,2,3]};func.call(null, 1, 2, 3);

當調用一個函數時,JavaScript的解譯器並不會計較形參和實參在數量、類型、以及順序上的區別,JavaScript的參數在內部就是用一個數組來表示的。從這個意義上說,apply比call的使用率更高,我們不必關心具體有多少參數被傳入函數,只要用apply一股腦地推過去就可以了。

當使用call或apply的時候,如果我們傳入的第一個參數為null,函數體內的this會指向預設的宿主對象,在瀏覽器中則是window:

var func = function(a, b, c){  console.log(this);};func.apply(null, [1, 2, 3]);  //輸出:window對象func.call(null, 1, 2, 3);  //輸出:window對象
call和apply的用途
  • 改變this指向

  • Function.prototype.bind

  • 借用其他對象的方法

1.改變this指向

call和apply最常見的用途是改變函數內部的this指向:

var A = {  name: ‘nameA‘;};var B = {  name: ‘nameB‘;};window.name = ‘nameWindow‘;var getName = function(){  conlole.log(this.name);};getName();  // 以普通函數調用,指向了window對象,輸出:nameWindowgetName.call(A);  // 改變了this的指向,指向了傳入的對象,輸出:nameAgetName.call(B);  // 改變了this的指向,指向了傳入的對象,輸出:nameB

2.Function.prototype.bind

大部分進階瀏覽器都實現了內建的Function.prototype.bind,用來指定函數內部的this指向。
若沒有原生的Function.prototype.bind實現,可以通過類比一個:

Function.prototype.bind = function(context){  var self = this;  // 儲存原函數  return function(){  // 返回一個新的函數      return self.apply(context, arguments);  // 執行新函數的時候,會把之前傳入的context當作新函數體內的this  }};var obj = { name: "objName"};var func = function(){  console.log(this.name);  // 輸出:objName}.bind(obj);func();

JavaScript設計模式與開發實踐 | this、call和apply

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.