JS中的this指針
1、JS中this指針指向
JS中函數的 this 并不遵守詞法作用域規則(即作用域由聲明時所處的位置決定),而是取決于函數的調用方式
影響 this 指針的因素有以下:
- 方法是否由某個對象調用,比如:obj.test()
- 是否是獨立函數調用,比如:test()
- 是否使用函數的call、apply、bind 等方法進行 this 的綁定,比如:test.apply(obj2)
- 是否是箭頭函數
- 是否使用了 new 關鍵字調用函數
2、作為獨立函數調用
在JS里面一般來說函數的調用 this 指向就是誰調用這個函數或方法,this 指針就指向誰。
function person(){ this.name="xl"; console.log(this); console.log(this.name); } person(); //輸出 window xl
在這段代碼中person函數作為獨立函數調用,實際上person是作為全局對象window的一個方法來進行調用的,即window.person();
所以這個地方是window對象調用了person方法,那么person函數當中的this即指window,同時window還擁有了另外一個屬性name,值為xl.
var name="xl"; function person(){ console.log(this.name); } person(); //輸出 xl
同樣這個地方person作為window的方法來調用,在代碼的一開始定義了一個全局變量name,值為xl,它相當于window的一個屬性,即window.name="xl",又因為在調用person的時候this是指向window的,因此這里會輸出xl.
3、由某個對象進行調用
在上面的代碼中,普通函數的調用即是作為window對象的方法進行調用。顯然this關鍵字指向了window對象.
再來看下其他的形式
var name="XL"; var person={ name:"xl", showName:function(){ console.log(this.name); } } person.showName(); //輸出 xl //這里是person對象調用showName方法,很顯然this關鍵字是指向person對象的,所以會輸出name var showNameA=person.showName; showNameA(); //輸出 XL //這里將person.showName方法賦給showNameA變量,此時showNameA變量相當于window對象的一個屬性,因此showNameA()執行的時候相當于window.showNameA(),即window對象調用showNameA這個方法,所以this關鍵字指向window
再換種形式:
var personA={ name:"xl", showName:function(){ console.log(this.name); } } var personB={ name:"XL", sayName:personA.showName } personB.sayName(); //輸出 XL //雖然showName方法是在personA這個對象中定義,但是調用的時候卻是在personB這個對象中調用,因此this對象指向
4、作為構造函數來調用
new 關鍵字調用函數會在函數內部創建一個新對象,然后將內部的 this 指針指向該對象,最后返回該對象。
function Person(name){ this.name=name; } var personA = Person("xl"); console.log(personA.name); // 輸出 undefined
console.log(window.name);//輸出 xl //上面代碼沒有進行new操作,相當于window對象調用Person("xl")方法,那么this指向window對象,并進行賦值操作window.name="xl". function Person(){
console.log(this)
}
new Person(); //輸出一個對象
new 操作符的作用過程解析:
1. 創建一個類的實例:創建一個空對象obj,然后把這個空對象的__proto__設置為Person.prototype(即構造函數的prototype);
2. 初始化實例:構造函數Person被傳入參數并調用,關鍵字this被設定指向該實例obj;
3. 返回實例obj。
function New(F){ var obj = {'__proto__': F.prototype}; /*第一步*/ return function() { F.apply(obj, arguments); /*第二步*/ return obj; /*第三步*/ } }
5、call、apply、bind方法的調用
5.1、call、apply方法
call 和 apply 都是為了改變某個函數運行時的上下文(context)而存在的,也就是為了改變函數體內部 this 的指向。
apply、call 兩個方法的作用完全一樣,只是接受參數的方式不太一樣。
var func = function(arg1, arg2) { }; func.call(anotherObj, arg1, arg2); func.apply(anotherObj, [arg1, arg2])
其中是 anotherObj 是你想指定的上下文,他可以是任何一個 JavaScript 對象,call 需要把參數按順序傳遞進去,而 apply 則是把參數放在數組里。
var name = "WL"; var Person = { name: "xl", showName: function () { console.log(this.name); } } Person.showName.call(); //輸出 "WL" //這里call方法里面的第一個參數為空,默認指向window。 //雖然showName方法定義在Person對象里面,但是使用call方法后,將showName方法里面的this指向了window。因此最后會輸出"WL"; funtion FruitA(n1, n2) { this.n1 = n1; this.n2 = n2; this.show = function (x, y) { this.n1 = x; this.n2 = y; } } var fruitA = new FruitA("cheery", "banana"); var FruitB = { n1: "apple", n2: "orange" }; fruitA.show.call(FruitB, "pear", "peach"); //FruitB調用fruitA的change方法,將fruitA中的this綁定到對象FruitB上。 console.log(FruitB.n1); //輸出 pear console.log(FruitB.n2); // 輸出 peach
5.2、bind()方法
var anotherFunc = func.bind( anotherObj, a1, a2 ); //anotherObj為新函數的 this,后面的參數在調用新函數時加上調用時傳入的參數作為函數的運行參數
bind() 方法會創建一個新函數,稱為綁定函數,當調用這個綁定函數時,綁定函數會以創建它時傳入 bind()方法的第一個參數作為 this,這個函數不論怎么調用都有同樣的this值。傳入 bind() 方法的第二個以及以后的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數。
var num = 9; var mymodule = { num: 10, getNum: function() { console.log(this.num); } };
mymodule.getNum(); // 10。誰調用函數則this為誰
var getNum = mymodule.getNum; getNum(); // 9, 因為在這個例子中,"this"指向全局對象
var boundGetNum = getNum.bind(mymodule); //用bind()方法改變上下文 boundGetNum(); // 10。通過bind返回了一個新函數的上下文,此時該函數的this是固定的
下面是一些例子:
var name = "WL"; function Person(name) { this.name = name; this.sayName = function () { setTimeout(function () { console.log("my name is " + this.name); }, 50) } } var person = new Person("xl"); person.sayName() //輸出 “my name is WL”。這里的setTimeout()定時函數,相當于window.setTimeout(),由window這個全局對象調用,因此this的指向為window, 則 this.name 則為 WL //使用bind()方法改變sayName方法 this.sayName = function () { setTimeout(function () { console.log("my name is " + this.name); }.bind(this), 50) //注意這個地方使用的bind()方法,綁定setTimeout里面的匿名函數的this一直指向Person對象 } person.sayName(); //輸出 “my name is xl”;
這里setTimeout(function(){console.log(this.name)}.bind(this),50);,匿名函數使用bind(this)方法后創建了新的函數,這個新的函數不管在什么地方執行,this都指向的Person,而非window,因此最后的輸出為"my name is xl"而不是"my name is WL"
在setTimeout中使用 bind 時有人可能會疑惑這時候綁定的 this 不也是 window 的嗎,其實不是,因為這是定義時的 this,使用時的 this 才是誰調用this指向的就是誰。
注意:setTimeout/setInterval/匿名函數執行的時候,this默認指向 window 對象,除非手動改變 this 的指向。在《javascript高級程序設計》當中,寫到:“超時調用的代碼 (setTimeout) 都是在全局作用域中執行的,因此函數中的this的值,在非嚴格模式下是指向window對象,在嚴格模式下是指向undefined”。本文都是在非嚴格模式下的情況。
另一種輸出 "xl" 的方法:
//輸出"WL" var name = "WL"; function Person() { this.name = "xl"; this.showName = function () { console.log(this.name); } setTimeout(this.showName, 50); } var person = new Person(); //輸出 "WL" //在setTimeout(this.showName,50)語句中,會延時執行this.showName方法,this.showName里面的this此時指向的是window對象,因此會輸出"WL"; //修改Person()函數 function Person() { this.name = "xl"; var that = this; this.showName = function () { console.log(that.name); } setTimeout(this.showName, 50) } var person = new Person(); //輸出 "xl" //這里在Person函數當中將this賦值給that,即讓that保存Person對象,因此在setTimeout(this.showName,50)執行過程當中,console.log(that.name)即會輸出Person對象的屬性"xl"
5.3、call、apply、bind的區別
call、apply、bind 三個方法都可以用來改變函數的this指向,具體區別如下:
call 和 apply 的區別:
- fn.call(newThis, params):call 函數的第一個參數是 this 的新指向,后面依次傳入函數fn要用到的參數。會立即執行 fn 函數。
- fn.apply(newThis,paramsArr):apply 函數的第一個參數是 this 的新指向,第二個參數是fn要用到的參數數組。會立即執行 fn 函數。
call、apply 和 bind 的區別:
fn.bind (newThis,params):bind 函數的第一個參數是 this 的新指向,后面依次傳入函數fn要用到的參數。不會立即執行fn函數,會返回一個新函數,該函數的 this 是固定的。且只能改變一次fn函數的指向,后續再用bind更改無效。
6、箭頭函數中的 this 指針
箭頭函數中沒有自己的 this ,只能繼承外層的 this 指針,這個稱為 this 穿透。
箭頭函數內部的 this 指的是定義時所處的作用域的外層作用域 。
var obj = { test() { console.log(this); var obj2 = { test2: () => { console.log(this); }, test3() { console.log(this); } } obj2.test2(); obj2.test3(); } } obj.test(); //在 test 方法中輸出的是 obj 對象 //在 test2 方法中輸出的也是 obj 對象 //在 test3 方法中輸出的是 obj2 對象
7、this 指針優先級
瀏覽器中的全局 this 無需跟其他元素比較,優先級最高。獨立函數調用和作為對象方法調用優先級最低。
其他的: new / 箭頭函數 > bind > apply / call
new 和箭頭函數不可同時使用

浙公網安備 33010602011771號