小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

徹底弄懂js中this指向(包含js綁定、優(yōu)先級、面試題詳解)

 愛睡覺的魄羅 2022-09-12 發(fā)布于北京

為什么要使用this

在javascript中,this可謂是無處不在,它可以用來指向某些元素、對象,在合適的地方使用this,能讓我們減少無用代碼的編寫

var user = {
  name: "aclie",
  sing: function () {
    console.log(user.name + '在唱歌')
  },
  dance: function () {
    console.log(user.name + '在跳舞')
  },
  study: function () {
    console.log(user.name + '在學習')
  },
}

以上這段代碼中,每個方法都需要用到user對象中的name屬性,如果當user對象名稱發(fā)生變化,那么所有方法都要改動,這種情況下,使用this是個很好的選擇

var user = {
  name: "aclie",
  sing: function () {
    console.log(this.name + '在唱歌')
  },
  dance: function () {
    console.log(this.name + '在跳舞')
  },
  study: function () {
    console.log(this.name + '在學習')
  },
}

this的指向

this的指向和函數(shù)在哪里定義無關,和如何調用有關

以下foo函數(shù)調用方式不同,this的值也不同

 function foo(){
  console.log(this)
}

foo()

var obj = {
  foo: foo
}
obj.foo() 

obj.foo.apply("hello")    

執(zhí)行結果如下圖所示

在這里插入圖片描述

this四種綁定方式

一、默認綁定

當函數(shù)獨立調用時,this默認綁定window

// 1、直接調用
function foo() {
  console.log(this)
}
foo()

// 2、對象中的函數(shù)
var obj1 = {
  foo: foo
}
var fn1 = obj1.foo
fn1()

// 3、被全局變量引用
var obj2 = {
  bar: function () {
    console.log(this)
  }
}
var fn2 = obj2.bar
fn2()

// 4、函數(shù)嵌套調用
function foo1() {
  console.log('foo1', this)
}
function foo2() {
  console.log('foo2', this)
  foo1()
}
function foo3() {
  console.log('foo3', this)
  foo2()
}
foo3()

// 5、通過閉包調用
var obj2 = {
  bar: function () {
    return function () {
      console.log(this)
    }
  }
}
obj2.bar()()

執(zhí)行結果如下

在這里插入圖片描述
以上五種調用方式全都屬于默認綁定,因為他們最終都是單獨的對函數(shù)進行調用

二、隱式綁定

調用的對象內部有對函數(shù)的引用

function foo() {
  console.log(this)
}

var obj1 = {
  name: 'obj1',
  foo: foo
}
obj1.foo()

var obj2 = {
  name: 'obj2',
  bar: function () {
    console.log(this)
  }
}
obj2.bar()

var obj3 = {
  name: 'obj3',
  baz: obj2.bar
}
obj3.baz()

以上代碼執(zhí)行結果為

在這里插入圖片描述
以上三種都屬于隱式綁定,他們都是通過對象調用,this就指向了該對象

三、顯式綁定

不希望在對象內部包含這個函數(shù)的引用,但又希望通過對象強制調用,使用call/apply/bind進行顯式綁定

function foo() {
  console.log(this)
}
var obj = {
  name: 'obj1',
}

foo.call(obj)
foo.apply(obj)
foo.call("xxx")

以上代碼的執(zhí)行結果為

在這里插入圖片描述
foo函數(shù)直接調用this應該指向window,這里通過call/apply來改變了this的指向

四、new綁定

通過new關鍵字來創(chuàng)建構造函數(shù)的實例,綁定this

function Person(name, age) {
  this.name = name
  this.age = age
}
const p1 = new Person('alice', 20)
const p2 = new Person('mogan', 24)
console.log(p1)
console.log(p2)

以上代碼的執(zhí)行結果如下

在這里插入圖片描述
此時this指向的是通過new創(chuàng)建的實例對象

this綁定的優(yōu)先級

一、隱式綁定高于默認綁定

function foo() {
  console.log(this)
}

var obj = {
  name: 'obj',
  foo: foo
}
obj.foo()

以上代碼執(zhí)行結果為

在這里插入圖片描述
foo函數(shù)默認綁定window對象,當同時存在隱式綁定和默認綁定時,隱式綁定優(yōu)先級高于默認綁定

二、顯示綁定高于隱式綁定

// 案例一
var user = {
  name: 'user',
  foo: function(){
    console.log(this)
  }
}
user.foo.call('kiki')

// 案例二
function foo() {
  console.log(this)
}
var obj = {
  name: "obj",
  foo: foo.bind("aclie")
}
obj.foo()

以上代碼的執(zhí)行結果為

在這里插入圖片描述
如果隱式綁定優(yōu)先級更高的話,this的指向應該都為對象,但根據(jù)以上執(zhí)行結果得知this綁定為顯示綁定的結果,所以當同時存在隱式綁定和顯示綁定時,顯示綁定的優(yōu)先級高于隱式綁定

三、new高于隱式綁定

var user = {
  name: 'lisa',
  foo: function () {
    console.log(this)
  }
}
new user.foo()

以上代碼的執(zhí)行結果如下

在這里插入圖片描述
當同時存在于new關鍵字綁定和隱式綁定時,this綁定了foo構造函數(shù),所以new關鍵字的優(yōu)先級高于隱式綁定

四、new高于顯示綁定

function bar(){
  console.log(this)
}

var fn = bar.bind('hello')
new fn()

以上代碼的執(zhí)行結果如下

在這里插入圖片描述
當同時存在于new關鍵字綁定和顯示綁定時,this綁定了bar構造函數(shù),所以new關鍵字的優(yōu)先級高于顯示綁定

綜上,以上四種綁定的優(yōu)先級順序為

new關鍵字 > 顯式綁定 > 隱式綁定 > 默認綁定

規(guī)則之外

還有幾種特殊的綁定方式,不在上述四種綁定規(guī)則中

一、忽略顯示綁定

當顯示綁定的值為 null/undefined 時,this直接綁定window

var user = {
  name: 'alice',
  foo: function () {
    console.log(this)
  }
}
user.foo()
user.foo.call(null)
user.foo.apply(undefined)

以上代碼執(zhí)行結果如下

在這里插入圖片描述
二、間接函數(shù)引用

var obj1 = {
  name: 'obj1',
  foo: function () {
    console.log(this)
  }
}
var obj2 = {
  name: 'obj2'
};
obj2.baz = obj1.foo;
obj2.baz();

(obj2.bar = obj1.foo)()

以上代碼的執(zhí)行結果為

在這里插入圖片描述

兩種方式所綁定的this不同,第二種方式進行了賦值調用,實際上是間接函數(shù)引用,(obj2.bar = obj1.foo)這里返回了賦值的結果,再加上一個小括號,就直接調用賦值的結果函數(shù)

三、箭頭函數(shù)

箭頭函數(shù)是不綁定this的,它的this來源于上級作用域

var user = {
  name: 'kiki',
  foo: () => {
    console.log('箭頭函數(shù)中的this',this)
  }
}
user.foo()

以上代碼的執(zhí)行結果如下

在這里插入圖片描述
這里調用foo函數(shù),因為箭頭函數(shù)不綁定this,所以去foo函數(shù)的上級查找this,找到了全局對象window

面試題

1、考察間接函數(shù)引用

var name = "window";
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  }
};
function sayName() {
  var sss = person.sayName;
  sss(); 
  person.sayName(); 
  (person.sayName)(); 
  (b = person.sayName)();
}
sayName();

執(zhí)行sayName函數(shù)

  • 變量sss 被person.sayName方法賦值,執(zhí)行sss函數(shù),此時是獨立函數(shù)調用,this指向全局window,全局中變量name被綁定到了window中,所以this.name為"window"
  • person.sayName() 為隱式綁定,this指向person對象,所以this.name為person.name,即"person"
  • (person.sayName)() 與前一個本質是一樣的,隱式綁定,this指向person對象,所以this.name為person.name,即"person"
  • (b = person.sayName)() 是間接函數(shù)引用,person.sayName賦值給b變量,而小括號括起來的代表賦值的結果,this指向window,this.name為window.name,即"window"

所以執(zhí)行結果為

在這里插入圖片描述

2、定義對象時是不產生作用域的

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person2 = { name: 'person2' }

person1.foo1();
person1.foo1.call(person2);

person1.foo2();
person1.foo2.call(person2);

person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);

person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);

調用過程分析

  1. foo1函數(shù)

    • person1.foo1() 隱式綁定,this指向person1,this.name為person1.name,即 “person1”
    • person1.foo1.call(person2) 隱式綁定+顯示綁定person2,顯示綁定優(yōu)先級更高,所以this指向person2,this.name為person2.name,即 “person2”
  2. foo2函數(shù)

    • person1.foo2() 隱式綁定, 箭頭函數(shù)沒有自己的this,所以向上層作用域查找,找到了全局window(person1是對象,定義它的時候不產生作用域),全局變量name被綁定到了window中,this.name為window.name,即 “window”
    • person1.foo2.call(person) 隱式綁定+顯示綁定,但是 箭頭函數(shù)不綁定this,這里的顯示綁定無效,沒有自己的this,向上層作用域查找,找到全局window,this.name為window.name,即 “window”
  3. foo3函數(shù)

    • person1.foo3()() 這里相當于執(zhí)行person1.foo()的返回函數(shù),這里是獨立函數(shù)調用,this指向全局window,this.name為window.name,即 “window”
    • person1.foo3.call(person2)() 這里通過call改變的是foo3函數(shù)中this的指向,但最終執(zhí)行的是foo3函數(shù)返回的閉包,閉包作為獨立函數(shù)調用,this仍然指向全局window,this.name為window.name,即’window"
    • person1.foo3().call(person2) 這里將foo3函數(shù)返回的閉包顯示綁定了person2對象,this指向person2,this.name為person2.name,即"person2"
  4. foo4函數(shù)

    • person1.foo4()() 執(zhí)行person1.foo()的返回值,返回的閉包是箭頭函數(shù)沒有this的,向上層作用域查找,找到了foo4函數(shù),foo4的this指向person1,所以閉包的this也指向person1,thiss.name為person1.name,即 “person1”
    • person1.foo4.call(person2)() 返回的閉包沒有this,向上層作用域找到了foo4函數(shù),foo4函數(shù)的this通過顯示綁定變成了person2,所以閉包的this也指向person2,this.name為person2.name,即"person2"
    • person1.foo4().call(person) 返回的閉包是箭頭函數(shù),無法通過call進行顯示綁定,直接向上級作用域查找,找到foo4函數(shù),foo4的this指向person1,所以閉包的this指向person1,this.name為person1.name,即"person1"

上述代碼的執(zhí)行結果如下

在這里插入圖片描述

3、構造函數(shù)中定義函數(shù),該函數(shù)的上級作用域是構造函數(shù)

var name = 'window'
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  },
  this.foo2 = () => console.log(this.name),
  this.foo3 = function () {
    return function () {
      console.log(this.name)
    }
  },
  this.foo4 = function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1()
person1.foo1.call(person2)

person1.foo2() 
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2) 

調用分析過程

  1. foo1函數(shù)

    • person1.foo1() 隱式綁定,this指向person1,person1創(chuàng)建實例時傳入name為person1,所以this.name為person1
    • person1.foo1.call(person2) 隱式綁定+顯示綁定,顯示綁定優(yōu)先級更高,綁定person2,person2創(chuàng)建實例時傳入的name為person2,所以this.name為person2
  2. foo2函數(shù)

    • person1.foo2() 隱式綁定,但foo2是箭頭函數(shù),沒有自己的this,向上層作用域查找,找到了Person構造函數(shù),此時this是指向person1這個對象的,而person1實例化時傳入的name為person1,所以this.name為person1
    • person1.foo2.call(person2) 隱式綁定+顯式綁定,但foo2是箭頭函數(shù),不綁定this,所以this仍然需要向上層作用域查找,找到Person構造函數(shù),this指向person1對象,所以this.name為person1
  3. foo3函數(shù)

    • person1.foo3()() 執(zhí)行person1.foo3的返回值,返回的函數(shù)是獨立調用,this指向window,全局的name變量被綁定到window中,this.name為window.name,即 “window”
    • person1.foo3.call(person2)() 顯式綁定更改的是foo3函數(shù)的this,最終執(zhí)行的是foo3函數(shù)的返回值,仍然是函數(shù)的獨立調用,所以this指向window,this.name為window.name,即 “window”
    • person1.foo3().call(person2) foo3函數(shù)的返回函數(shù)通過顯示綁定將this綁定到了person2中,person2創(chuàng)建實例時傳入的name為person2,所以this.name為person2
  4. foo4函數(shù)

    • person1.foo4()() 執(zhí)行foo4函數(shù)的返回值,返回函數(shù)為箭頭函數(shù),沒有this,所以向上層作用域查找,找到foo4函數(shù)的this指向person1,所以箭頭函數(shù)的this也指向person1,所以this.name為person1
    • person1.foo4.call(person2)() foo4通過顯示綁定將this綁定成了person2,返回的函數(shù)為箭頭函數(shù),this與父級作用域foo4一致,所以箭頭函數(shù)的this也指向person2,所以this.name為person2
    • person1.foo4().call(person2) foo4函數(shù)的返回值為箭頭函數(shù),不綁定this,這里顯示綁定無效,向上級作用域查找this,找到foo4函數(shù),this指向person1

執(zhí)行結果如下

在這里插入圖片描述

4、區(qū)分作用域

var name = 'window'
function Person (name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()() 
person1.obj.foo1.call(person2)() 
person1.obj.foo1().call(person2) 
person1.obj.foo2()() 
person1.obj.foo2.call(person2)() 
person1.obj.foo2().call(person2) 
  1. foo1函數(shù)

    • person1.obj.foo1()() 執(zhí)行foo1函數(shù)的返回函數(shù),此時該函數(shù)為獨立函數(shù)調用,this指向window,全局變量name被添加到window中,這里的this.name指向window.name,即 “window”
    • person1.obj.foo1.call(person2)() 這里顯示綁定改變foo1中this的指向,但最終執(zhí)行的是foo1函數(shù)的返回值,返回函數(shù)作為獨立函數(shù)調用,this仍然指向window,所以this.name為window.name,即 “window”
    • person1.obj.foo1().call(person2) 這里通過顯示綁定更改foo1函數(shù)的返回函數(shù)中this的指向, 所以該函數(shù)this指向person2,而person2在實例化的時候傳入name值為person2,所以this.name為person2
  2. foo2函數(shù)

    • person1.obj.foo2()() 執(zhí)行foo2的返回函數(shù),此時該函數(shù)為獨立函數(shù)調用,但它自己沒有this,要向上級作用域查找,找到foo2函數(shù)的this指向obj,所以該函數(shù)的this也指向obj,this.name為obj.name,即 “obj”
    • person1.obj.foo2.call(person2)() 執(zhí)行foo2的返回函數(shù),此時該函數(shù)為獨立函數(shù)調用,但它自己沒有this,要向上級作用域查找,foo2函數(shù)的this通過顯示綁定變成person2,所以該函數(shù)的this也為person2,而person2在實例化的時候傳入name值為person2,所以this.name為person2
    • person1.obj.foo2().call(person2) foo2的返回函數(shù)為箭頭函數(shù),不綁定this,顯式綁定無效,也沒有自己的this,要向上級作用域查找,找到foo2函數(shù)的this指向obj,所以該函數(shù)的this也指向obj,this.name為obj.name,即 “obj”

所以執(zhí)行結果為

在這里插入圖片描述

以上就是關于this指向的理解,關于js高級,還有很多需要開發(fā)者掌握的地方,可以看看我寫的其他博文,持續(xù)更新中~

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多