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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

javascript中令人迷惑的this

發布時間:2023/12/2 javascript 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 javascript中令人迷惑的this 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

JS中的this很多時候會讓人捉摸不透,不知道這個this到底指向的是什么。現在根據自己的理解寫下這篇文章做一個總結。

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

默認綁定和隱式綁定

首先看最常見的調用方式,通過對象調用這個函數或者叫方法(this隱式綁定到了調用函數的對象上)。

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

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

var fn1 = obj.fn fn1()

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

現在我們切換到嚴格模式:

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

我們發現執行fn1的時候報了一個錯誤Uncaught TypeError: Cannot read property 'a' of undefined,這是因為在嚴格模式下fn1中的this指向的undefined,獲取undefined的屬性當然會報錯,因為undefined不是一個對象也不能隱式轉換成一個對象。

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

顯示調用call和apply的綁定

上面無論是通過對象還是直接通過函數名調用函數,其中的this指向誰好像編譯器心里有數是一種默契。那么我們能不能不要這個默契,我們自己來指定函數調用的時候this指向誰。我們通過call方法和apply方法就可以輕易做到。

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

我們可以看到又打印出了1,不負所望。調用fn1的時候我們通過結果可以知道函數體內的this被綁定到了obj上。apply做的事情和call是一樣的,區別就在于傳入函數中參數的形式,call必須要和調用函數一樣一個一個傳入參數,但是apply允許我們通過一個數組將需要的參數一起傳入函數中。這個神奇的功能就像是ES6中的 … 操作符。

bind的綁定

bind的也可以綁定函數中的this,但是和上面的call和apply有明顯的不同,call和apply是直接就執行了函數,但是bind不是,bind會返回一個函數,這個特性就讓這個bind不僅僅可以綁定this,還可以進行函數柯里化。

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的簡單Polyfill

if (!Function.prototype.bind) {Function.prototype.bind = function (obj) {// 這一句檢測this是不是函數我本以為是多余,但是bind是可以被call的,這時候this就很有可能不指向functionif (typeof this !== 'function') {throw new TypeError('不是函數的數據嘗試調用bind方法!')}// obj 就是函數要綁定的this,而函數就是現在函數體中的this,因為bind函數是在Function.prototype上的// 這是在獲取在bind的時候就傳過來的參數var args = [].slice.call(arguments, 1)// 存一下需要bind的函數var fn = this // 處理fn函數的prototype屬性var _fn = function () {}// 這個函數將被返回var bindFn = function () {// 處理一下被bind的函數使用new調用的時候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還考慮到了參數的拼接,還有函數的prototype屬性的處理,還包括被bind的函數作為構造函數調用的時候其中this的指向。

new的this綁定

可以綁定this的不僅僅是上面的call,apply和bind,new也可以的。我們知道通過new調用一個函數的時候會有下面幾個步驟:

  • 新建一個對象
  • 將對象的原型關聯到函數的原型屬性
  • 將this指向這個對象
  • 執行函數
  • 如果函數沒有返回值則返回這個對象
  • 看上面第二步就將函數中的this關聯到了新建的對象上了。那么對于一個bind的函數我們使用new來調用函數中的this到底是指向了new新建出來的對象還是bind時候的對象呢?

    其實上面bind方法的Polyfill已經給出了答案,是會指向new新建出來的對象。fn.apply(this instanceof bindFn ? this : obj, args.concat([].slice.call(arguments))),這里通過this instanceof bindFn判斷是不是通過new調用的該方法,如果是那么就指向當前的this也就是新建的對象,如果不是才指向傳進來的obj。

    ##綁定的優先級

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

    隱藏是綁定優先級要高于默認綁定并且低于顯示綁定的call和apply方法。

    所以整理出來的優先級如下:

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

    關于箭頭函數

    ES6的箭頭函數和上面說的情況都不一樣,箭頭函數中的this指向并不是在調用的時候確定的,而是在定義的時候,和定義的時候的詞法作用域有關,并且后期并不能通過上面顯示綁定的方法修改this的指向。也就是說箭頭函數定義的時候拿到當前上下文的this,然后就不會再改變了。

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

    上面打印出了1 和 obj 對象

    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對象,而不是window。call方法并沒能修改箭頭函數的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()

    結果和call的綁定一致沒有改變箭頭函數中的this。

    那么能使用new呢?箭頭函數不能使用new調用,會報錯的。

    參考

    你不知道的javascript上卷

    總結

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

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。