參考
【第1318期】深入淺出 JavaScript 關鍵詞 -- this
https://juejin.cn/post/684490...
寫這篇文章原因
因為總結面經中遇到了this的指向問題,這也是面試官常問的基礎問題。所以單獨列出來供參考學習
this指向幾點判斷規則
js的函數除了聲明定義的形參之外,每個函數還接受兩個附加參數:this和arguments。this 的值並不是由函數定義放在哪個對象裏面決定,而是函數執行時由誰來喚起決定。
- 函數調用模式:strict情況下,指向undefined,否則指向全局變量
- 方法調用模式:指向方法所在的對象
- 構造函數模式調用,用
new時,this指向新構建的對象 - apply,call明確綁定: 或者
bind綁定,this指向被綁定的對象 -
箭頭函數內部的
this就是外層代碼塊的this,包裹在函數中時this指向函數調用時所在的對象,若放在全局中則是指向全局對象window,且不會改變// Q1 規則1:如果一個函數中有this,但是它沒有被上一級的對象所調用,那麼this指向的就是window var a = 1; function print () { console.log(this.a) } print() //a=1 // Q2 規則2:如果一個函數中有this,這個函數有被上一級的對象所調用,那麼this指向的就是上一級的對象。 const obj = { a: 2, print: function () { console.log(this.a) } } obj.print(); //2 // Q3 const obj = { a: 3, print: function () { console.log(this.a) } } obj.print() //3 與下面的foo()區別在於是全局環境還是obj環境 const foo = obj.print; foo() //undefined 全局情況下調用的,沒有全局變量a // Q3.1 如果一個函數中有this,這個函數中包含多個對象,儘管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象 const obj = { a: 3, printOut:{ a:4, printIn: function(){ console.log(this.a)}, }, } obj.printOut.printIn() //4 const foo = obj.printOut.printIn; foo() //undefined 全局情況下調用的,沒有全局變量a // Q4 Q4-Q9參見規則5 const obj = { a: 4, print: () => { console.log(this.a) } } obj.print(); //undefined 有箭頭函數,對象不構成單獨的作用域,導致`print`箭頭函數定義時的作用域就是全局作用域。 // Q5 var a = 5 const obj = { a: 6, print: () => { console.log(this.a) } } obj.print.call({a: 7}); //5 理由同上 // Q6 function Person () { this.a = 8 this.print = function () {console.log(this.a)} return {a: 9} } const p = new Person() console.log(p.a) //9 此時p為構造函數返回的{a:9} console.log(p.print()) // undefined p是沒有print函數的 // Q7 'use strict'; var a = 1; function print () { console.log(this.a) } print() //undefined 嚴格模式下,this指向undefined // Q8 普通函數 function foo() { setTimeout(function() { console.log('id:', this.id); }); } var id = 21; foo.call({ id: 42 }); //21 setTimeout是延遲函數,在foo被調用後才執行,這時候this指向window //Q9 箭頭函數,匿名函數定義時所在的執行環境就是foo函數,所以匿名函數內部的this執向始終會和foo函數的this執向保持一致,指向call的對象。 為什麼指向call的對象呢?參見call的用法:函數名.call(對象,arg1,arg2,...argn),功能是調用函數,並把函數內部的this指向第一個參數的對象,然後將第二個及之後的是所有的參數,作為實參傳給函數。 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo.call({ id: 42 }); //42 function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } var id = 21; foo(); //21(沒有用call) // Q10 var A = function( name ){ this.name = name; }; var B = function(){ A.apply(this,arguments); }; B.prototype.getName = function(){ return this.name; }; var b=new B('sven'); console.log( b.getName() ); // 輸出: 'sven'
---更新,今天看了閉包,有了新的認識
//1
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
var a = object.getNameFunc;
a()();//這裏的調用者一直是window,因此輸出"The Window";
object.getNameFunc()();//這裏object.getNameFunc()時調用者是object,但是將object.getNameFunc()作為一個整體,他的調用者是window,是window來調用object.getNameFunc()(),此時調用者是window,因為輸出"The Window";
//2
var name = "The Window";
var object1 = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
object1.getNameFunc()();//這裏類似上面object.getNameFunc()時調用者是object的,但是在object1.getNameFunc()的返回函數中,將this賦值給that,因此object1.getNameFunc()()中,this仍然是object,所以輸出"My Object",
var e = object1.getNameFunc;
e()();//這裏this一直是window,因此不管如何賦值,都輸出"The Window"
//3
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return this.name;
}
};
object.getNameFunc()
//"My Object"
(object.getNameFunc)()
//"My Object"
(object.getNameFunc = object.getNameFunc)()
//"The Window"
//最後一個object.getNameFunc的值是函數本身。function (){return this.name;}