JavaScript允許在函數內部,引用當前執行中環境的其他變數。
function func() {
console.log(a)
}
如上代碼,func函數引用了當前執行環境的a,問題是這個函數可以在任何執行環境中被調用,因此這時候的a可能就指向不同了,所以JS引擎需要有一個機制,可以依靠其優雅的、準確地指向當前代碼運行的執行環境。
//假設這個object名稱很長,且有可能改名
let objectWithALongName={
name:"David",
func1(){
return objectWithALongName.name;
},
func2(){
return this.name;
}
}
objectWithALongName的方法func2使用了this關鍵字,就優雅多了,然後即使往後對象名稱改變了,func2內部代碼也不需改變。
另外如果有其他執行環境也需要使用到這個函數,使用this也可以優雅的指向該執行環境:
let otherObject = {
name:"John",
cloneMethod:objectWithALongName.func2
}
otherObject.cloneMethod(); // John func2中的this這時候指到otherObject
this可以精準的指向某個對象。
//全域變數
let objectWithALongName = {
name: "David"
};
(function() {
//局部變數
let objectWithALongName = {
name: "John",
func1() {
return objectWithALongName.name;
},
func2() {
//相對於func1,這邊用了this可以清楚知道指向內部IIFE的執行環境
return this.name;
}
};
console.log(objectWithALongName.func1());// John
})();
呼叫位子就是函數在代碼中在哪裡被調用,而不是聲明的位子。所以搞清楚「由誰在哪調用」才能精確的找到this的指向。
let module = {
id: 10,
getId: function() {
return this.id;
}
}
console.log(module.getId());//>> 10
let globalGetId = module.getId;//getX和module.getX都是指向記憶體中函数的地址而已,這邊並沒有被()執行
console.log(globalGetId()); //>> undefined
作為module對象的getId方法被調用,this指向module,module對象有一個屬性id值為10,所以console.log(module.getX())出10,而globalGetId中的this會指向全域,全域中並沒有定義id,所以出undefined。
-->This既不指向函數自身,也不指向函數的作用域
let obj = {
a:"123"
}
function func() {
this = obj; //報錯,因為在執行階段試圖改變this
console.log(this.a);
}
func();
function func() {
console.log( this.a ); // this指向全域對象
}
let a = 2;
func(); // 2
對於默認指向來說,決定this指向的並不是調用位置是否是嚴格模式(use strict),而是函數本體是否處於嚴格模式。如果函數處於嚴格模式,this指向undefined,否則指向全域。
function func() {
"use strict";//函数中處於嚴格模式下,this指向undefined
console.log(this.a);
}
let a = 123;
func(); // 報錯
function func() {
console.log(this.a);
}
var a = 123;
(function() {
"use strict";
func(); // 123
})();
隱式指向是日常開發中最常見的,也就是決定this指向的是由誰呼叫,與函數存在的位置無關:
function func() {
console.log(this.a);
}
let obj = {
a: 2,
func: func
};
obj.func(); // 2
// 找到呼叫位置,由obj對象来呼叫函數func,
// 此時可以說函數func被呼叫時,obj對象擁有或者包含func函數
// 所以此時的 this 指向呼叫 func 函数的 obj 對象。
對象屬性引用鏈中只有最頂層或者說最後一層才會影響調用位置,簡單說,this指向最靠近被調用函數的對象。
function func() {
console.log(this.a);
}
let obj2 = {
a: 10,
func: func
};
let obj1 = {
a: 20,
obj2: obj2
};
// this指向obj2對象,因為obj2離的最近
obj1.obj2.func(); // 10
Javascript內建對象Function的三種原型方法call()、apply()、bind(),他們的第一個參數是對象,他們會把這個對象綁定到this,接著在調用函數時讓this指向這個對象。
let a = 10;
function func() {
console.log( this.a );
}
let obj = {
a:20
};
func.call(obj); // 20
// 在呼叫func時強制把this指向obj
使用new來調用函數,或者說發生構造函數調用時,會執行底下操作 :
function func(a) {
this.a = a;
}
let bar = new func("test");
console.log(bar.a); // test
// 使用new 來呼叫func(..)时,創建一個新對象並把它绑定到func(..)调用中的this上
另外一個觀點:
function User(name) {
// this = {};(隱藏創建)
// 添加属性到 this
this.name = name;
this.isAdmin = false;
// return this;(隱藏返回)
}
用以上介紹規則可以簡單做一個判斷流程:
如果是的話,this綁定的是新創建的對象。
function func(name) {
this.name = name;
this.getName = function() {
return this.name;
};
}
let obj = new func("apple"); //this會指向obj
console.log(obj.getName()); // apple
let obj1 = {
name: "David"
};
function func() {
return this.name; //這裡的this本来指向window
}
let str = func.call(obj1); //改變了func函数裡面this的指向,指向obj1
console.log(str); // David
let obj1 = {
name: "Jack",
func() {
return this.name; //指向obj1
}
};
//這裡的obj1.func(),表明func函数被obj1调用,因此func中的this指向obj1
console.log(obj1.func()); // Jack
如果在非嚴格模式下,就綁定到全域對象。
let a = 123; //為全域對象增加一個變數a
function func() {
return this.a;
}
console.log(func()); // 123