1. 程式人生 > 其它 >jquery如何拿到一個物件_分享一個關於this物件的程式設計小技巧,如何使用箭頭函式避免this物件混淆?...

jquery如何拿到一個物件_分享一個關於this物件的程式設計小技巧,如何使用箭頭函式避免this物件混淆?...

技術標籤:jquery如何拿到一個物件

以微信小程式舉例。小程式的主要語言是js,使用小程式也方便說明我們接下來要講的問題。

首先,實現一個正常的效果:

wxml:bind:tap=js:startAnimate(){  this.setData({    percentValue:0  })  this.setData({    percentValue:100  })}

效果:

63034a59c7747caf993a47dbd4dfd123.png

沒問題。

接下來需求改變一下,先彈出一個模態對話方塊。在使用者單擊“確定”後,再開始動畫。

程式碼:

wxml:bind:tap=js:onTap(e){  // 模態彈窗  wx.showModal({    title: 'start?',    success(res){      if (res.confirm) {        console.log('使用者點選確定')        this.startAnimate()      } else if (res.cancel) {        console.log('使用者點選取消')      }    }  })},startAnimate(){  this.setData({    percentValue:0  })  this.setData({    percentValue:100  })}

現在執行就有問題了,除錯區爆出了一個錯誤:

Cannot read property 'startAnimate' of undefinedTypeError: Cannot read property 'startAnimate' of undefined

動畫也沒有如期啟動。為什麼?

因為在小程式介面的success回撥函式中,簡寫等於帶function關鍵字的寫法。例如:

wx.showModal({title:'提示',  success (res) {...  }})

相當於:

wx.showModal({  title: '提示',success:function(res){    ...  }})

而是非同步回撥的function中,this物件並非指表面上我們看到的wx.showModal程式碼所在的物件。

解決這個問題的方法也很簡單,只要換用箭頭函式就可以了:

onTap(e){  wx.showModal({    title: 'start?',    success:(res)=>{      ...    }  })}

在這個程式碼中,只是將原來的 success(res) 改成了 success:(res)=> 。

為什麼使用箭頭可以呢?

因為在箭頭函式中,this物件與封閉詞法環境中的this保持一致。換一句話,箭頭函式中的this,是定義與執行它的函式中this物件。或者我們可以理解為,箭頭函式是沒有this物件的。箭頭函式中的this,取決於它身處何處。

那麼,回顧一下,this是什麼?

在非全域性作用域下指代“當前”物件

this是當前程式碼上下文執行環境中的一個屬性,是一個在執行時確定身份,同時又不能在編碼時指定的一個動態物件。

一般我們都是在一個函式或方法中使用this,這個時候this指代什麼,本質上取決於當前函式是由誰呼叫的。例如看下面這段程式碼:

// 取決於誰呼叫它let obj = {  prop: 60,  func: function() {    return this.prop;  }};console.log(obj.func()); // 60

在這段程式碼中,func是由obj呼叫的,所以在func方法內部,this就指代obj這個物件。

考慮一種特殊的情況,那麼在全域性作用作用域下,this指代誰呢?

在全域性作用域下this指代全域性物件

如果函式是全域性函式,是在全域性使用域中呼叫的,那麼this等於全域性物件。這個全域性物件,在瀏覽器宿主環境中指window物件。在微信小程式宿主環境中,沒有window物件,全域性物件在預設專案配置下是undefined。在Node.js宿主環境中,全域性物件是global。

這是在沒有開啟嚴格模式的情況下,假如我們開啟了嚴格模式又如何呢?

嚴格模式

對於這個程式碼:

function f1(){  return this;}f1()===window;//在瀏覽器中全域性物件是window//輸出true

但是如果僅新增一行宣告:

"use strict";function f1(){  return this;}f1() === window; //在瀏覽器中全域性物件是window// 輸出 false

輸出結果就由true變成了false。

這是因為"use strict"開啟了js的嚴格模式,在嚴格模式下,全域性函式中的this等於undefined。

上文中我們提到,小程式中預設沒有全域性物件。這句話其實不全面。小程式在專案預設開啟了ES6轉ES5功能的情況下,是自動啟用嚴格模式的,所以這個時候我們測試程式碼,輸出的是undefined。

但如果我們將專案配置中的“ES6轉ES5”反選,將輸出一個window物件。為什麼會輸出window物件?不是說小程式宿主環境中沒有window物件嗎?

iOSJavaScriptCoreWKWebView安卓             V8        chromium定製核心小程式開發者工具  NWJS      Chrome WebView

這是由於我們是用微信開發者工具測試的緣故。微信三端,包括iOS、Android與開發者工具,對小程式執行環境的實現並不一致。在開發者工具中,渲染是基於Chrome WebView實現的,這實際上仍然是一個瀏覽器的宿主環境。

但是在手機上測試,這個值打印出來是不一樣的。

this物件雖然不能在編碼時賦值,但是有其它方法變換this物件。bind、call、apply這三個方法都可以。接下來我們看一看,如何用bind解決本文開始遇到的問題。

程式碼:

onTap(e){  let startAnimate = this.startAnimate.bind(this)  // 模態彈窗  wx.showModal({    title: 'start?',    success(res){      if (res.confirm) {        console.log('使用者點選確定')        startAnimate()      } else if (res.cancel) {        console.log('使用者點選取消')      }    }  })},

只需要在bind第一個引數傳遞this物件,就可以了。apply與call的使用方法是型別的,也是在第一個引數的地方傳遞this物件;不同處在於bind只繫結不執行,而後兩者是馬上執行的。

本質上這三個方法改變的是方法的呼叫者,所以方法內部的this也改變了。

最後總結一下,雖然bind等方法可以改變方法的呼叫者物件,藉此改變this物件。但在大多數情況下,我們使用不捆綁this的箭頭函式,來避免this物件的混淆問題,是最簡單省事的方法。


11月7日