日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

javascript中令人迷惑的this

發(fā)布時(shí)間:2023/12/2 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 javascript中令人迷惑的this 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

JS中的this很多時(shí)候會(huì)讓人捉摸不透,不知道這個(gè)this到底指向的是什么。現(xiàn)在根據(jù)自己的理解寫下這篇文章做一個(gè)總結(jié)。

我們知道this指向誰(shuí)一般情況下是在運(yùn)行時(shí)決定的,并且運(yùn)行時(shí)決定this指向的因素又有很多,例如是不是被bind了,或者調(diào)用的時(shí)候使用了apply和call這類方法,還有是不是通過new來(lái)調(diào)用這個(gè)函數(shù),如果沒有以上顯示綁定,那么是obj.fn()這樣調(diào)用的嗎?或者直接fn()?如果直接fn()調(diào)用,那么fn的函數(shù)體是嚴(yán)格模式嗎?最后這個(gè)函數(shù)是ES6中的箭頭函數(shù)嗎?

默認(rèn)綁定和隱式綁定

首先看最常見的調(diào)用方式,通過對(duì)象調(diào)用這個(gè)函數(shù)或者叫方法(this隱式綁定到了調(diào)用函數(shù)的對(duì)象上)。

var a = 2 var obj = {a: 1,fn: function () {console.log(this.a)} }obj.fn()

我們通過運(yùn)行知道打印了1,也就是說(shuō)這時(shí)fn中的this指向了調(diào)用他的obj,但是是否表示任何情況下fn中的this都是指向fn定義時(shí)的對(duì)象obj呢?顯然不是的。在某些情況下這種隱式綁定的this會(huì)丟失,如下:

var fn1 = obj.fn fn1()

上面打印了2,是的出乎意料并沒有打印1,所以關(guān)于this的指向和函數(shù)的定義沒有什么關(guān)系,看似函數(shù)fn屬于對(duì)象obj,其實(shí)并不是。這時(shí)fn中this默認(rèn)指向的是window對(duì)象。上面通過賦值就弄丟了原本的隱式綁定,沒有了隱式綁定,只能使用默認(rèn)綁定。

現(xiàn)在我們切換到嚴(yán)格模式:

'use strict' var a = 2 var obj = {a: 1,fn: function () {console.log(this.a)} }var fn1 = obj.fn fn1()

我們發(fā)現(xiàn)執(zhí)行fn1的時(shí)候報(bào)了一個(gè)錯(cuò)誤Uncaught TypeError: Cannot read property 'a' of undefined,這是因?yàn)樵趪?yán)格模式下fn1中的this指向的undefined,獲取undefined的屬性當(dāng)然會(huì)報(bào)錯(cuò),因?yàn)閡ndefined不是一個(gè)對(duì)象也不能隱式轉(zhuǎn)換成一個(gè)對(duì)象。

注:上面通過將對(duì)象的方法賦值給一個(gè)變量導(dǎo)致函數(shù)的方法中默認(rèn)綁定this丟失,這種情況會(huì)出現(xiàn)在很多其他意想不到的地方,例如函數(shù)的傳參(這也是一種隱式的賦值)。

顯示調(diào)用call和apply的綁定

上面無(wú)論是通過對(duì)象還是直接通過函數(shù)名調(diào)用函數(shù),其中的this指向誰(shuí)好像編譯器心里有數(shù)是一種默契。那么我們能不能不要這個(gè)默契,我們自己來(lái)指定函數(shù)調(diào)用的時(shí)候this指向誰(shuí)。我們通過call方法和apply方法就可以輕易做到。

var a = 2 var obj = {a: 1,fn: function () {console.log(this.a)} } var fn1 = obj.fn fn1.call(obj)

我們可以看到又打印出了1,不負(fù)所望。調(diào)用fn1的時(shí)候我們通過結(jié)果可以知道函數(shù)體內(nèi)的this被綁定到了obj上。apply做的事情和call是一樣的,區(qū)別就在于傳入函數(shù)中參數(shù)的形式,call必須要和調(diào)用函數(shù)一樣一個(gè)一個(gè)傳入?yún)?shù),但是apply允許我們通過一個(gè)數(shù)組將需要的參數(shù)一起傳入函數(shù)中。這個(gè)神奇的功能就像是ES6中的 … 操作符。

bind的綁定

bind的也可以綁定函數(shù)中的this,但是和上面的call和apply有明顯的不同,call和apply是直接就執(zhí)行了函數(shù),但是bind不是,bind會(huì)返回一個(gè)函數(shù),這個(gè)特性就讓這個(gè)bind不僅僅可以綁定this,還可以進(jìn)行函數(shù)柯里化。

var a = 2 var obj = {a: 1,fn: function () {console.log(this.a)} } var fn1 = obj.fn var fn2 = fn1.bind(obj) fn2()

不出意料的也打印了1。

bind的簡(jiǎn)單Polyfill

if (!Function.prototype.bind) {Function.prototype.bind = function (obj) {// 這一句檢測(cè)this是不是函數(shù)我本以為是多余,但是bind是可以被call的,這時(shí)候this就很有可能不指向functionif (typeof this !== 'function') {throw new TypeError('不是函數(shù)的數(shù)據(jù)嘗試調(diào)用bind方法!')}// obj 就是函數(shù)要綁定的this,而函數(shù)就是現(xiàn)在函數(shù)體中的this,因?yàn)閎ind函數(shù)是在Function.prototype上的// 這是在獲取在bind的時(shí)候就傳過來(lái)的參數(shù)var args = [].slice.call(arguments, 1)// 存一下需要bind的函數(shù)var fn = this // 處理fn函數(shù)的prototype屬性var _fn = function () {}// 這個(gè)函數(shù)將被返回var bindFn = function () {// 處理一下被bind的函數(shù)使用new調(diào)用的時(shí)候return fn.apply(this instanceof bindFn ? this : obj, args.concat([].slice.call(arguments)))} // 處理fn.prototypeif (fn.prototype) {_fn.prototype = fn.prototype}bindFn.prototype = new _fn()return bindFn} }

上面的兼容是類似mozilla的寫法,不僅僅綁定了this還考慮到了參數(shù)的拼接,還有函數(shù)的prototype屬性的處理,還包括被bind的函數(shù)作為構(gòu)造函數(shù)調(diào)用的時(shí)候其中this的指向。

new的this綁定

可以綁定this的不僅僅是上面的call,apply和bind,new也可以的。我們知道通過new調(diào)用一個(gè)函數(shù)的時(shí)候會(huì)有下面幾個(gè)步驟:

  • 新建一個(gè)對(duì)象
  • 將對(duì)象的原型關(guān)聯(lián)到函數(shù)的原型屬性
  • 將this指向這個(gè)對(duì)象
  • 執(zhí)行函數(shù)
  • 如果函數(shù)沒有返回值則返回這個(gè)對(duì)象
  • 看上面第二步就將函數(shù)中的this關(guān)聯(lián)到了新建的對(duì)象上了。那么對(duì)于一個(gè)bind的函數(shù)我們使用new來(lái)調(diào)用函數(shù)中的this到底是指向了new新建出來(lái)的對(duì)象還是bind時(shí)候的對(duì)象呢?

    其實(shí)上面bind方法的Polyfill已經(jīng)給出了答案,是會(huì)指向new新建出來(lái)的對(duì)象。fn.apply(this instanceof bindFn ? this : obj, args.concat([].slice.call(arguments))),這里通過this instanceof bindFn判斷是不是通過new調(diào)用的該方法,如果是那么就指向當(dāng)前的this也就是新建的對(duì)象,如果不是才指向傳進(jìn)來(lái)的obj。

    ##綁定的優(yōu)先級(jí)

    通過上面bind的Polyfill我們知道new綁定的this優(yōu)先級(jí)要大于顯示綁定的bind,并且bind的綁定優(yōu)先級(jí)要高于call和apply方法。

    隱藏是綁定優(yōu)先級(jí)要高于默認(rèn)綁定并且低于顯示綁定的call和apply方法。

    所以整理出來(lái)的優(yōu)先級(jí)如下:

    new > bind > (apply == call) > 隱式綁定 > 默認(rèn)綁定

    關(guān)于箭頭函數(shù)

    ES6的箭頭函數(shù)和上面說(shuō)的情況都不一樣,箭頭函數(shù)中的this指向并不是在調(diào)用的時(shí)候確定的,而是在定義的時(shí)候,和定義的時(shí)候的詞法作用域有關(guān),并且后期并不能通過上面顯示綁定的方法修改this的指向。也就是說(shuō)箭頭函數(shù)定義的時(shí)候拿到當(dāng)前上下文的this,然后就不會(huì)再改變了。

    var a = 2 var obj = {a: 1,fn: function () {console.log(this.a)var b = () => {console.log(this)}b()} } obj.fn()

    上面打印出了1 和 obj 對(duì)象

    var a = 2 var obj = {a: 1,fn: function () {console.log(this.a)var b = () => {console.log(this)}b.call(window)} } obj.fn()

    雖然使用了call指定this綁定,但是還是打印了1和obj對(duì)象,而不是window。call方法并沒能修改箭頭函數(shù)的this指向。

    var a = 2 var obj = {a: 1,fn: function () {console.log(this.a)var b = () => {console.log(this)}var c = b.bind(window)c()} } obj.fn()

    結(jié)果和call的綁定一致沒有改變箭頭函數(shù)中的this。

    那么能使用new呢?箭頭函數(shù)不能使用new調(diào)用,會(huì)報(bào)錯(cuò)的。

    參考

    你不知道的javascript上卷

    總結(jié)

    以上是生活随笔為你收集整理的javascript中令人迷惑的this的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。