jquery如何拿到一個物件_分享一個關於this物件的程式設計小技巧,如何使用箭頭函式避免this物件混淆?...
技術標籤:jquery如何拿到一個物件
以微信小程式舉例。小程式的主要語言是js,使用小程式也方便說明我們接下來要講的問題。
一
首先,實現一個正常的效果:
wxml:bind:tap=js:startAnimate(){ this.setData({ percentValue:0 }) this.setData({ percentValue:100 })}
效果:
沒問題。
二
接下來需求改變一下,先彈出一個模態對話方塊。在使用者單擊“確定”後,再開始動畫。
程式碼:
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日