1. 程式人生 > >今天來給大家分析js中this的指向幾種情況?

今天來給大家分析js中this的指向幾種情況?

        之前在寫程式碼時遇到this時,總是在考慮應不應該用,原因是當時分不清楚,後來把this的所有情況分析了一遍,其實超級簡單,而且他的使用場景很好,下面我來把他的幾種情況分析一下,如果this分佈太清得小夥伴們可以參考哈~希望對你們有幫助!

        與其他語言相比,函式this在 JavaScript 中的表現略有不同,此外,在嚴格模式和非嚴格模式之間也會有一些差別.在絕大多數情況下,函式的呼叫方式決定了this的值。this不能在執行期間被賦值,並且在每次函式被呼叫時this的值也可能會不同。ES5引入了bind方法來設定函式的this值,而不用考慮函式如何被呼叫的,ES2015 引入了支援this

詞法解析的箭頭函式(它在閉合的執行上下文內設定this的值)。

(1)無論是否在嚴格模式下,在全域性執行上下文中(在任何函式體外部)this 都指代全域性物件。

// 在瀏覽器中, window 物件同時也是全域性物件:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

(2)在函式內部,this的值取決於函式被呼叫的方式

function f1(){
  return this;
}
//在瀏覽器中:
f1() === window;   //在瀏覽器中,全域性物件是window

//在Node中:
f1() === global;

然而,在嚴格模式下,this將保持他進入執行上下文時的值,所以下面的this將會預設為undefined

function f2(){
  "use strict"; // 這裡是嚴格模式
  return this;
}

f2() === undefined; // true

如果要想把 this 的值從一個上下文傳到另一個,就要用 call 或者apply 方法。

// 將一個物件作為call和apply的第一個引數,this會被繫結到這個物件。
var obj = {a: 'Custom'};

// 這個屬性是在global物件定義的。
var a = 'Global';

function whatsThis
(arg) { return this.a; // this的值取決於函式的呼叫方式 } whatsThis(); // 'Global' whatsThis.call(obj); // 'Custom' whatsThis.apply(obj); // 'Custom'

當一個函式在其主體中使用 this 關鍵字時,可以通過使用函式繼承自Function.prototype 的 callapply 方法將 this 值繫結到呼叫中的特定物件。

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
使用 call 和 apply 函式的時候要注意,如果傳遞給 this 的值不是一個物件,JavaScript 會嘗試使用內部 ToObject 操作將其轉換為物件。因此,如果傳遞的值是一個原始值比如 7'foo',那麼就會使用相關建構函式將它轉換為物件,所以原始值 7 會被轉換為物件,像 new Number(7) 這樣,而字串 'foo' 轉化成 new String('foo') 這樣,例如:
function bar() {
  console.log(Object.prototype.toString.call(this));
}

//原始值 7 被隱式轉換為物件
bar.call(7); // [object Number]

(3)ECMAScript 5 引入了 。呼叫f.bind(someObject)會建立一個與f具有相同函式體和作用域的函式,但是在這個新函式中,this將永久地被繫結到了bind的第一個引數,無論這個函式是如何被呼叫的。

function f(){
  return this.a;
}

var g = f.bind({a:"azerty"});
console.log(g()); // azerty

var h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azerty

var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
(4)箭頭函式

箭頭函式中,this與封閉詞法上下文的this保持一致。在全域性程式碼中,它將被設定為全域性物件:

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true


// 接著上面的程式碼
// 作為物件的一個方法呼叫
var obj = {foo: foo};
console.log(obj.foo() === globalObject); // true

// 嘗試使用call來設定this
console.log(foo.call(obj) === globalObject); // true

// 嘗試使用bind來設定this
foo = foo.bind(obj);
console.log(foo() === globalObject); // true

注意:如果將this傳遞給callbind、或者apply,它將被忽略。不過你仍然可以為呼叫新增引數,不過第一個引數(thisArg)應該設定為null

無論如何,foo 的 this 被設定為他被建立時的上下文(在上面的例子中,就是全域性物件)。這同樣適用於在其他函式內建立的箭頭函式:這些箭頭函式的this被設定為封閉的詞法上下文的。

(5)當函式作為物件裡的方法被呼叫時,它們的 this 是呼叫該函式的物件。下面的例子中,當 o.f()被呼叫時,函式內的this將繫結到o物件。

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

console.log(o.f()); // logs 37

(6)對於在物件原型鏈上某處定義的方法,同樣的概念也適用。如果該方法存在於一個物件的原型鏈上,那麼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

在這個例子中,物件p沒有屬於它自己的f屬性,它的f屬性繼承自它的原型。雖然在對 f 的查詢過程中,最終是在 o 中找到 f 屬性的,這並沒有關係;查詢過程首先從 p.f 的引用開始,所以函式中的 this 指向p。也就是說,因為f是作為p的方法呼叫的,所以它的this指向了p。這是 JavaScript 的原型繼承中的一個有趣的特性。

(7)再次,相同的概念也適用於當函式在一個 getter 或者 setter 中被呼叫。用作 getter 或 setter 的函式都會把 this 繫結到設定或獲取屬性的物件。

function sum() {
  return this.a + this.b + this.c;
}

var o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return (this.a + this.b + this.c) / 3;
  }
};

Object.defineProperty(o, 'sum', {
    get: sum, enumerable: true, configurable: true});

console.log(o.average, o.sum); // logs 2, 6

(8)當一個函式用作建構函式時(使用new關鍵字),它的this被繫結到正在構造的新物件。

雖然構造器返回的預設值是this所指的那個物件,但它仍可以手動返回其他的物件(如果返回值不是一個物件,則返回this物件)。

/*
 * 建構函式這樣工作:
 *
 * function MyConstructor(){
 *   // 函式實體寫在這裡
 *   // 根據需要在this上建立屬性,然後賦值給它們,比如:
 *   this.fum = "nom";
 *   // 等等...
 *
 *   // 如果函式具有返回物件的return語句,
 *   // 則該物件將是 new 表示式的結果。 
 *   // 否則,表示式的結果是當前繫結到 this 的物件。
 *   //(即通常看到的常見情況)。
 * }
 */

function C(){
  this.a = 37;
}

var o = new C();
console.log(o.a); // logs 37


function C2(){
  this.a = 37;
  return {a:38};
}

o = new C2();
console.log(o.a); // logs 38

(9)當函式被用作事件處理函式時,它的this指向觸發事件的元素(一些瀏覽器在使用非addEventListener的函式動態新增監聽函式時不遵守這個約定)。

// 被呼叫時,將關聯的元素變成藍色
function bluify(e){
  console.log(this === e.currentTarget); // 總是 true

  // 當 currentTarget 和 target 是同一個物件時為 true
  console.log(this === e.target);        
  this.style.backgroundColor = '#A5D9F3';
}

// 獲取文件中的所有元素的列表
var elements = document.getElementsByTagName('*');

// 將bluify作為元素的點選監聽函式,當元素被點選時,就會變成藍色
for(var i=0 ; i<elements.length ; i++){
  elements[i].addEventListener('click', bluify, false);
}

(10)當代碼被內聯on-event 處理函式呼叫時,它的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時指向的預設物件)。

OK今天的內容就分享到這裡啦,