JavaScript中this指標指向的徹底理解
現在我們就來總結一下js中this的指向。首先需要了解一下幾個概念:
1、全域性變數預設掛載在window物件下
2、一般情況下this指向它的呼叫者
3、es6的箭頭函式中,this指向建立者,並非呼叫者
4、通過call、apply、bind可以改改變this的指向
在非箭頭函式下, this 指向呼叫其所在函式的物件,而且是離誰近就是指向誰(此對於常規物件,原型鏈, getter & setter等都適用);建構函式下,this與被建立的新物件繫結;DOM事件,this指向觸發事件的元素;內聯事件分兩種情況,bind繫結, call & apply 方法等。
下面我們具體分析一下
1、在函式呼叫時
//非嚴格模式下 const func = function () { console.log(this); const func2 = function () { console.log(this); }; func2(); //Window }; func(); //Window //嚴格模式下 'use strict' const func = function () { console.log(this); const func2 = function () { console.log(this); }; func2(); //undefined }; func(); //undefined
2、作為物件方法
const user = { userName: '小張', age: 18, selfIntroduction: function () { const str = '我的名字是:' + this.userName + ",年齡是:" + this.age;
console.log(this);//user console.log(str); const loop = function() {
console.log(this); //Window console.log('我的名字是:' + this.userName + ",年齡是:" + this.age); }; loop(); //我的名字是:undefined,年齡是:undefined } }; user.selfIntroduction(); //我的名字是:小張,年齡是:18
按照第二條規則,this指向他的呼叫者,selfIntroduction()方法的呼叫者是user,所以在selfIntroduction()方法內部this指向了他的父物件即user,而loop方法輸出的為undefined的原因就是我在上面所說的javascript的設計缺陷了,在這種情況下,我們通常選擇在selfIntroduction()方法裡將this快取下來。
const user = { userName: '小張', age: 18, selfIntroduction: function () { const str = '我的名字是:' + this.userName + ",年齡是:" + this.age; console.log(str); const that=this; const loop = function () { console.log('我的名字是:' + that.userName + ",年齡是:" + that.age); }; loop(); //我的名字是:小張,年齡是:18 } }; user.selfIntroduction(); //我的名字是:小張,年齡是:18
此時loop的this指向就理想了。
const user={ userName:'小張', age:18, selfIntroduction:function(){ const str='我的名字是:'+this.userName+",年齡是:"+this.age; console.log(str); } }; const other =user.selfIntroduction; other(); //我的名字是:undefined,年齡是:undefined const data={ userName:'小李', age:19, }; data.selfIntroduction=user.selfIntroduction; data.selfIntroduction(); //我的名字是:小李,年齡是:19
在看這段程式碼,將selfIntroduction()賦值給了全域性變數other,呼叫other()方法,other掛載在全域性函式window物件下,window物件下沒有userName 和 age 這兩個屬性,所以輸出為undefined。
第二段程式碼,申明瞭data物件,包含了username和age屬性,記住我們的第二條規則一般情況下this指向它的呼叫者,大家就明白了,data是selfIntroduction()的函式的呼叫者,所以輸出了data的userName和age。
3:在html裡作為事件觸發
<body> <div id="btn">點選我</div> </body> const btn=document.getElementById('btn'); btn.addEventListener('click',function () { console.log(this); //<div id="btn">點選我</div> })
在種情況其實也是遵循了第二條規則一般情況下this指向它的呼叫者,this指向了事件的事件源即event。
4:new關鍵字(建構函式)
建構函式中的this與被建立的新物件繫結。
注意:當構造器返回的預設值是一個this引用的物件時,可以手動設定返回其他的物件,如果返回值不是一個物件,返回this。
const fun=function(userName){ this.userName=userName; } const user=new fun('郭德綱'); console.log(user.userName); //郭德綱
function C(){ this.a = 37; } var o = new C(); console.log(o.a); // logs 37 function C2(){ this.a = 37; return {a:38}; } var b = new C2(); console.log(b.a); // logs 38
5:es6(箭頭函式)
const func1=()=>{ console.log(this); }; func1(); //Window const data={ userName:'校長', selfIntroduction:function(){ console.log(this); //Object {userName: "校長", selfIntroduction: function} const func2=()=>{ console.log(this); //Object {userName: "校長", selfIntroduction: function} } func2(); } } data.selfIntroduction();
大家在看看我開頭說的第三條準則:es6的箭頭函式中,this指向建立者,並非呼叫者,fun1 在全域性函式下建立,所以this指向全域性window,而fun2在物件data下建立,this指向data物件,所以在func2函式內部this指向data物件,個人認為es6的箭頭函式的this指向是對我上面所說的javascript設計缺陷的改進,(個人認知)。
6:改變this的指向
當函式通過Function物件的原型中繼承的方法 call() 和 apply() 方法呼叫時, 其函式內部的this值可繫結到 call() & apply() 方法指定的第一個物件上, 如果第一個引數不是物件,JavaScript內部會嘗試將其轉換成物件然後指向它。
function add(c, d){ return this.a + this.b + c + d; } var o = {a:1, b:3}; add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 function tt() { console.log(this); } // 返回物件見下圖(圖1) tt.call(5); // Number {[[PrimitiveValue]]: 5} tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}
而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