原文連接javascript
與其餘語言相比,函數的this
關鍵字在JavaScript中的行爲略有不一樣。而且它在嚴格模式和非嚴格模式之間也有一些區別。html
在絕大多數狀況下,函數的調用方式決定了this
的值。this
不能在執行期間被賦值,在每次函數被調用時this
的值也可能會不一樣。ES5引入了bind
方法來設置函數的this
值,而不用考慮函數如何被調用的。java
var name = "caibaojian.com"; var person = { name: "kang", pro: { name: "Michael", getName: function() { return this.name; } } }; console.log(person.pro.getName()); // Michael var pepole = person.pro.getName; console.log(pepole()); // caibaojian.com
var name = "caibaojian.com"; var person = { name: "kang", pro: { name: "Michael", getName: function() { console.log(this); return this.name; } } }; console.log(person.pro.getName()); // Object { name: "...", getName: () }, Michael var pepole = person.pro.getName; console.log(pepole()); // Window, caibaojian.com
'use strict'; var name = "caibaojian.com"; var person = { name: "kang", pro: { name: "Michael", getName: function() { console.log(this); return this.name; } } }; console.log(person.pro.getName()); // Object { name: "...", getName: () }, Michael var pepole = person.pro.getName; console.log(pepole()); // undefined
var name = "caibaojian.com", person = { name : "kang", getName : function(){ return function(){ return this.name; }; } }; console.log(person.getName()()); // caibaojian.com
this
的用法:在全局運行上下文中(在任何函數體外部),this
指代全局對象,不管是否在嚴格模式下。在瀏覽器中,全局對象爲window
對象。面試
console.log(this.document === document); // true console.log(this === window); // true this.a = 37; console.log(window.a); // 37
在函數內部,this
的值取決於函數是如何調用的。瀏覽器
在非嚴格模式下,this
的值不會在函數執行時被設置,此時的this
的值會默認設置爲全局對象。app
function foo(){ return this; } foo() === window; // true
在嚴格模式下,this
將保持他進入執行環境時的值,因此下面的this
將會默認爲undefined
函數
function foo(){ "use strict"; // 嚴格模式 return this; } foo() === undefined; // true
在嚴格模式下,若是this
未被執行的上下文環境定義,那麼它將會默認爲undefined
。this
this
當以對象裏的方法的方式調用函數時,它們的this
是調用該函數的對象。prototype
下面的例子中,當obj.f()
被調用時,函數內的this
將綁定到obj
對象。code
var obj = { prop: 37, foo: function() { return this.prop; } }; console.log(obj.foo()); // 37
注意,在何處或者如何定義調用函數徹底不會影響到this
的行爲。
在上一個例子中,咱們在定義obj
的時候爲其成員foo
定義了一個匿名函數。可是,咱們也能夠首先定義函數而後再將其附屬到obj.foo
。這樣作this
的取值也和上面一致:
var obj = { prop: 37 }; function independent() { return this.prop; } obj.foo = independent; console.log(obj.foo()); // 37
這說明this
的值只與函數foo
做爲obj
的成員被調用有關係。
相似的,this
的綁定只受最靠近的成員引用的影響。
在下面的這個例子中,咱們把一個方法g
看成對象obj.b
的函數調用。在此次執行期間,函數中的this
將指向obj.b
。事實上,這與對象自己的成員沒有多大關係,最靠近的引用纔是最重要的。
var obj = { prop: 37 }; function independent() { return this.prop; } obj.b = { g: independent, prop: 42 }; console.log(obj.b.g()); // 42
this
相同的概念在定義在原型鏈中的方法也是一致的。若是該方法存在於一個對象的原型鏈上,那麼this
指向的是調用這個方法的對象,表現得好像是這個方法就存在於這個對象上同樣。
var obj = { f : function(){ return this.a + this.b; } }; var p = Object.create(obj); p.a = 1; p.b = 4; console.log(p.f()); // 5
在這個例子中,對象p
沒有屬於它本身的f
屬性,它的f
屬性繼承自它的原型。可是這對於最終在obj
中找到f
屬性的查找過程來講沒有關係;查找過程首先從p.f
的引用開始,因此函數中的this
指向p
。也就是說,由於f
是做爲p
的方法調用的,因此它的this
指向了p
。這是JavaScript的原型繼承中的一個有趣的特性。
getter
與setter
中的this
相同的概念也適用時的函數做爲一個getter
或者setter
調用。做爲getter
或setter
函數都會綁定this
到從設置屬性或獲得屬性的那個對象。
function modulus(){ return Math.sqrt(this.re * this.re + this.im * this.im); } var obj = { re: 1, im: -1, get phase(){ return Math.atan2(this.im, this.re); } }; Object.defineProperty(obj, 'modulus', { get: modulus, enumerable: true, configurable: true }); console.log(obj.phase, obj.modulus); // -0.785 1.414
this
當一個函數被做爲一個構造函數來使用(使用new
關鍵字),它的this
與即將被建立的新對象綁定。
注意:當構造器返回的默認值是一個this
引用的對象時,能夠手動設置返回其餘的對象,若是返回值不是一個對象,返回this
。
function Fn(){ this.a = 37; } var obj = new Fn(); console.log(obj.a); // 37 function Foo(){ this.a = 37; return { a: 38 }; } obj = new Foo(); console.log(obj.a); // 38
call
和apply
當一個函數的函數體中使用了this
關鍵字時,經過全部函數都從Function
對象的原型中繼承的call()
方法和apply()
方法調用時,它的值能夠綁定到一個指定的對象上。
function add(c, d){ return console.log(this.a + this.b + c + d); } var obj = { a: 1, b: 3 }; add.call(obj, 5, 7); // 1 + 3 + 5 + 7 = 16 add.apply(obj, [10, 20]); // 1 + 3 + 10 + 20 = 34
使用call
和apply
函數的時候要注意,若是傳遞的this
值不是一個對象,JavaScript將會嘗試使用內部 ToObject
操做將其轉換爲對象。所以,若是傳遞的值是一個原始值好比7
或foo
,那麼就會使用相關構造函數將它轉換爲對象,因此原始值7
經過new Number(7)
被轉換爲對象,而字符串foo
使用new String('foo')
轉化爲對象,例如:
function bar() { console.log(Object.prototype.toString.call(this)); } bar.call(7); // [object Number]
bind()
方法ECMAScript 5引入了Function.prototype.bind
。調用fn.bind(someObject)
會建立一個與fn
具備相同函數體和做用域的函數,可是在這個新函數中,this
將永久地被綁定到了bind
的第一個參數,不管這個函數是如何被調用的。
function fn(){ return this.a; } var g = fn.bind({ a: "azerty" }); console.log(g()); // "azerty" var obj = { a: 37, foo: fn, go: g }; console.log(obj.foo(), obj.go()); // 37, "azerty"
this
當函數被用做事件處理函數時,它的this
指向觸發事件的元素(一些瀏覽器在動態添加監聽器時不遵照這個約定,除非使用addEventListener
)。
// 獲取文檔中的全部元素的列表 var elements = document.getElementsByTagName('*'); for(var i = 0; i < elements.length; i++){ // 當元素被點擊時,就會變成藍色 elements[i].addEventListener('click', function (e) { e.stopPropagation(); console.log(this); console.log(e.currentTarget); console.log(e.target); // 上面3個值是同樣的 this.style.backgroundColor = '#A5D9F3'; }); }
this
當代碼被內聯處理函數調用時,它的this
指向監聽器所在的DOM元素:
<button onclick="alert(this.tagName.toLowerCase());"> Show this </button>
上面的alert
會顯示button
。注意只有外層代碼中的this
是這樣設置的:
<button onclick="alert((function(){return this})());"> Show inner this </button>
在這種狀況下,沒有設置內部函數的this
,因此它指向global/window
對象(即非嚴格模式下調用的函數未設置this
時指向的默認對象)。