譯文:javascript function中的this


個人理解+google翻譯。如有錯誤,請留言指正。原文來自MDN: this

簡介

Javascript中一個函數的this關鍵字的行為相對其它語言有些不同。在嚴格模式和非嚴格模式間也有區別。

在大多數情況下,this的值由函數如何調用來決定。this值不能在函數執行過程中賦值設置,並且每次函數調用時this值可能也不相同。ES5通過添加bind方法設置函數的this值,無論函數如何被調用。(this值永久不變)

全局上下文中

全局執行環境中(函數外部),無論在與不在嚴格模式下this指向全局對象。

console.log(this.document === document); //true
//在web瀏覽器中,window對象即是全局對象:
console.log(this === window); // true
this.a=37;
console.logn(window.a); //37

function上下文

在函數內部,this值依賴於函數如何調用。

簡單調用

function f2(){
    "use strict";//使用嚴格模式
    return this;
}
f2() === undefined;

在上面的例子中,this值沒有在函數調用的時候被設置。由於代碼沒有在嚴格模式下,this值肯定總會是默認為全局對象。

function f2(){
    "use strict";//使用嚴格模式
    return this;
}
f2() === undefined;

嚴格模式下,this值保持在進入執行環境時設置的值。如果this值未定義,則為undefined;this值也能被設置為任意值:如null、42或"I am not this";

注意:在第二例子中,this值應為undefined,因為f2函數被調用時沒有提供任何引用[原文:base],(例如:window.f2())。這一特性在某些瀏覽器初始支持嚴格模式時沒有實現,造成返回錯誤的對象:window

function作為對象方法

當函數作為對象方法調用時,該函數的this值設為調用該方法的對象。

在下例中,當o.f()被調用,函數內部this綁定為o對象(inside the function this is bound to the o object).

var o={
    prop:37;
    f:function(){
        return this.prop;
    }
};
console.log(o.f());// 37

注意:this的這種特性不受其函數如何定義或函數的定義位置的影響。在上例,我們把函數定義為對象o的內部成員f。然而我們也可以輕易的先定義這個函數,后面再把它附加到o.f上,結果是一樣的。

var o ={prop:37};
function independent{
    return this.prop;
}
o.f=independent;
console.log(o.f());//37

這段代碼表明:函數的調用來自對象o的f方法;同樣,this的綁定只受最近參考成員的影響。在下面的例子里,調用該函數的時候,則為對象o.b的g方法。此次函數執行時,函數內的this指向o.b.事實上,與o.b對象是o對象的成員沒有任何關系,最近參考才是重要的。

var o ={prop:37};
function independent{
    return this.prop;
}
o.f=independent;
o.b{prop:42,g:independent};
console.log(o.f());//37
console.log(o.b.g());//42

function在原型鏈上

同樣的概念也適用於在原型鏈上某些地方定義方法。如果該方法定義在對象的原型鏈上,this指向調用該方法的對象。【假設該方法是對象的成員】

var o = {f:function(){return this.a + this.b}};
var p = Object.create(o);
p.a=1;
p.b=4;
console.log(p.f());//5

在上面的例子中,對象o分配給變量p,p沒有自己的屬性f,p從其原型中繼承f屬性。原型中f屬性的查找最終會在對象o上以一個成員的身份找到;查找開始以p.f為參考,所以函數內的this取值為對象p。由於f作為p的方法被調用,this指向p,這Javascript原型繼承一個有意思的特性。

個人添加了一個Object.create()例子,方便自我解惑:詳情請參考MDN:Object.create()

o = Object.create(Object.prototype, {//對象內屬性對應於Object.defineProperties的第二個參數
        // foo is a regular "value property" --foo是一個普通值屬性
      foo: { writable:true, configurable:true, value: "hello" },
      // bar is a getter-and-setter (accessor) property --bar是一個getter、setter存取屬性
    bar: {
        configurable:false,//默認:false,屬性的類型能被修改或該屬性可以被刪除時為true
        enumerable:false,//默認:false,對象屬性枚舉屬性時顯示該屬性為true
        //writable:false, //默認:false,當屬性值能通過賦值操作而改變時為true
        //value:"hi",//與屬性相關聯的值。可以是任何有效的JavaScript值(數字,對象,函數等)。
        get: function() { return this.value || 10 },//以一個函數作為getter方法的屬性,如果沒有指定函數則為undined。返回其屬性值
        set: function(value) {this.value=value}//以一個函數作為setter方法的屬性,如果沒有指定函數則為undined。接收新的參數值成為屬性值
    }
});
console.log(o.bar);//10
o.bar="hi";
console.log(o.bar); //hi

bar定義中有兩行注釋,如果取消注釋符號,在Firefox下會報錯:不允許在指定get 或 set時 指定其value屬性或為定義writable屬性。[writable為true或false都會報錯]

function作為(Object.defineProperty中的)getter方法或setter方法

同樣的概念再次適用於函數作為getter方法或setter方法。函數用作getter或setter方法則其this綁定到已經設置set或set屬性的所屬對象。(原文: A function used as getter or setter has its this bound to the object from which the property is being set or gotten.)

function modulus(){
    return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o ={
    re : 1,
    im : -1,
    get phase(){//ES5的寫法,還是建議用__defineGetter__這種API比較好,ie7 8 不支持
        return Math.atan2(this.im,this.re);
    },
    set txt(value){
        this.re=value.re;
        this.im=value.im;
    }
};
Object.defineProperty(o,'modulus1',{get : modulus,enumerable : true,configurable : true});//ie7 不支持此方法 ie8 不支持get:
console.log(o.phase,o.modulus1);// -0.78 1.414
o.txt={re:12,im:3};//o.txt({re:12,im:3});報錯:o.txt is not a function
console.log(o.phase,o.modulus1);//0.24 12.36

ES5 的getter、setter方法在ie7 8上不支持,汗的沒汗了……

function作為構造函數

當函數通過new關鍵字做為構造函數時,函數的this指向該構造函數實例化后的對象。

注意:構造函數默認返回的對象被this引用,構造函數也可以返回其它對象。如果返回值不是一個對象,那么返回默認this引用的對象。( 原文:while the default for a constructor is to return the object referenced by this, it can instead return some other object (if the return value  isn't an object, then the this object is returned))

/*
 * Constructors work like this:構造函數以類似下面的方式運行:
 *
 * function MyConstructor(){
 *   // Actual function body code goes here.  Create properties on |this| as
 *   // desired by assigning to them.  E.g.,
 *   // 譯:實際的代碼放在這里,通過 this 創建需要的屬性及賦值。如:
 *   this.fum = "nom";
 *   // et cetera...譯:其它代碼             
 *   // If the function has a return statement that returns an object, that
 *   // object will be the result of the |new| expression.  Otherwise, the
 *   // result of the expression is the object currently bound to |this|
 *   // (i.e., the common case most usually seen).
 *   //如果構造函數通過return語句返回一個對象,這個對象將會通過new表達式返回的實際對象。
 *   //如果函數體內沒有return語句,則通過new表達式返回的對象為當前this默認引用的對象(這也是我們一般常見的情況)
 * }
 */
 function c(){
     this.a=37;
 }
 var o = new c();
 console.log(o.a);//37
 function c2(){
     this.a = 37;
     return {a:38};
 }
 o = new c2();
 console.log(o.a);//38

在c2函數中,因為在構造函數運行並返回了一個對象,this默認綁定的對象則被簡單的丟棄。(實際上是使語句this.a = 37;成為廢棄代碼。也沒有完全被廢棄,因為它得到運行,只是在沒有外部影響的情況下而被排除。)

通過call或apply方法調用

函數內使用this關鍵字,通過call或apply方法調用該函數時,this值被指定為一個特定的對象,全部function繼承於Function.prototype(all functions inherit from Function.prototype)

function add(c,d){
    return this.a + this.b + c + d;
}
var o ={a:1,b:3};
//第一個參數是一個對象作為this,隨后的參數作為函數調用參數
add.call(o,5,7); //1+3+5+7 = 16
//第一個參數是一個對象作為this,第二個參數是一個數組,該數組成員做函數調用參數
add.apply(o,[10,20]);//1+3+10+20 = 34

ES5 bind 方法

ES5 添加Function.prototype.bind。調用f.bind(someObject)創建與f具有相同內容和作用域的新函數,但原來的函數中的this在新函數中永久指向bind方法第一個參數,不管該函數如何調用。

function f(){
    return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); //azerty
var o ={a:37,f:f,g:g};
console.log(o.f(),o.g()); // 37,azerty

作為DOM事件處理函數

當function作為事件處理函數,this指向觸發該事件的DOM元素。(有些瀏覽器不支持下面通過addEventListener方法為監聽動態添加方法,如:IE)。

//作為監聽器時 改變相關元素為藍色
function bluify(e){
    console.log(this === e.currentTarget);     //一直為true
    console.log(e.currentTarget);
    console.log(this === e.target);            //當currentTarge和target為同一對象時為true
    console.log(e.target);
    this.style.backgroundColor="#A5D9F3";
    //e.stopPropagation(); //阻止事件冒泡
}
//獲取document下元素列表
var elements = document.body.getElementsByTagName('*');
//添加bluify方法作為點擊事件監聽器,使元素被點擊后變為藍色
for(var i = 0;i < elements.length; i++){
    elements[i].addEventListener('click',bluify,false);
}

上面的代碼在某個結構層疊在3層或以上的頁面下的控制台中(firebug/google chrome console)運行一下,會表現出一種怪異現象,無論點哪個元素,整個頁的背景全部變為藍色。試着把代碼e.stopPropagation()的注釋取消掉,或審查下元素,就應該知道為什么了。還不懂?請搜索 javascript 事件冒泡 相關資料。

在內聯事件處理中

當代碼在內聯處理器中執行時,代碼中的this被設為其上有事件監聽的DOM元素:

<button onclick="alert(this.tagName.toLowerCase());">Show this</button>

上面的警告彈出框顯示button。 Note however that only the outer code has its this set this way:【實在翻譯不出來了,將就的這么翻譯吧:但是要注意,這種方式只有函數外面的代碼中的this值才會被設置為事件監聽DOM元素】

<button onclick="alert((function(){return this}()));">Show inner this</button>

這種情況下,函數內部的this沒有被指定,所以該函數返回全局對象或window對象。(即非嚴格模式下調用代碼中的this沒有被指定時的默認對象)。

function's this總結(自加)

  • 指向全局對象
  1. 函數體外部,script標簽內直接調用this
  2. 簡單調用函數(非嚴格模式下)
  • 指向對象
  1. 函數作為對象方法(無論函數是否在對象內定義),this指向調用該方法的最近參考對象
  2. 函數在對象的原型鏈上
  3. 函數作ES5下對象的getter或setter方法
  • 指向實例化對象或普通(hash)對象
  1. 函數作為構造函數或構造函數內返回一個普通對象
  • 指向特定對象
  1. 函數被call或apply調用。如:fun.call(obj,param),fun.apply(obj,[param1,param2])
  • 永久指向一個對象(ES5)
  1. 函數通過bind方法永久指向一個對象,無論該函數如何調用
  • 指向事件觸發DOM元素
  1. 函數作為事件處理器(動態添加)
  2. 在內聯事件處理器代碼中(如果有function(){},則為function的代碼外部)的this,如<button onclick="alert(this.tagName.toLowerCase());functiong(){//代碼}"></button>

PS:用腦子里的半吊子英語水平+google翻譯+有道翻譯 這三種工具耗時3天按個人理解翻譯出這篇文章。總算體會到了IT英語翻譯的苦悶之處,不過自己也算有了翻譯的經驗,小自豪一把。如有錯誤,請列位拍磚指正。謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM