前幾篇文章中,咱們主要講了merge options的一些操做。今天咱們回到init方法往下講。vue
if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm }
上面的代碼邏輯很簡單,主要就是爲Vue實例的_renderProxy屬性賦值。不一樣的代碼運行環境賦值的結果不一樣。es6
接下來看initProxy方法究竟怎麼樣代理vm屬性呢?編程
initProxy = function initProxy (vm) { if (hasProxy) { // determine which proxy handler to use const options = vm.$options const handlers = options.render && options.render._withStripped ? getHandler : hasHandler vm._renderProxy = new Proxy(vm, handlers) } else { vm._renderProxy = vm } }
先來看hasProxy的代碼函數
const hasProxy = typeof Proxy !== 'undefined' && Proxy.toString().match(/native code/)
從變量的名字咱們知道它的做用就是判斷當前環境中Proxy是否可用。同窗們若是不熟悉Proxy的用法,能夠點擊這裏。這個判斷方法在咱們平時寫代碼中也能夠進行借鑑。
回到代碼中,若是當前環境存在Proxy,則執行塊內的語句。ui
const options = vm.$options const handlers = options.render && options.render._withStripped ? getHandler : hasHandler
上面兩行代碼的邏輯是,若是options上存在render屬性,且render屬性上存在_withStripped屬性,則proxy的traps(traps其實也就是自定義方法)採用getHandler方法,不然採用hasHandler方法。更多關於traps相關的內容可點擊這裏。
接下來看看getHandler和hasHandler方法spa
const getHandler = { get (target, key) { if (typeof key === 'string' && !(key in target)) { warnNonPresent(target, key) } return target[key] } }
getHandler方法主要是針對讀取代理對象的某個屬性時進行的操做。當訪問的屬性不是string類型或者屬性值在被代理的對象上不存在,則拋出錯誤提示,不然就返回該屬性值。
該方法能夠在開發者錯誤的調用vm屬性時,提供提示做用。代理
const hasHandler = { has (target, key) { const has = key in target const isAllowed = allowedGlobals(key) || key.charAt(0) === '_' if (!has && !isAllowed) { warnNonPresent(target, key) } return has || !isAllowed } }
hasHandler方法的應用場景在於查看vm實例是否擁有某個屬性。好比調用for in循環遍歷vm實例屬性時,會觸發hasHandler方法。
首先使用in操做符判斷該屬性是否在vm實例上存在。code
const has = key in target
接下來經過下列語句,看屬性名稱是否可用?對象
const isAllowed = allowedGlobals(key) || key.charAt(0) === '_'
allowedGlobals的定義以下ip
const allowedGlobals = makeMap( 'Infinity,undefined,NaN,isFinite,isNaN,' + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + 'require' // for Webpack/Browserify )
咱們結合makeMap函數一塊兒來看
export function makeMap ( str: string, expectsLowerCase?: boolean ): (key: string) => true | void { const map = Object.create(null) const list: Array<string> = str.split(',') for (let i = 0; i < list.length; i++) { map[list[i]] = true } return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val] }
先來分析makeMap,其做用是經過傳入的string參數來生成映射表。如傳入下列參數
'Infinity,undefined,NaN,isFinite,isNaN,' ....
經過makeMap方法能夠生成下面這樣的一個映射表
{ Infinity: true, undefined: true ...... }
這個方法很是有用,咱們在平常寫代碼的時候也能夠進行借鑑。
回到hasHandler源碼中。allowedGlobals最終存儲的是一個表明特殊屬性名稱的映射表。
因此結合has和isAllowed屬性,咱們知道當讀取對象屬性時,若是屬性名在vm上不存在,且不在特殊屬性名稱映射表中,或沒有以_符號開頭。則拋出異常。
最後回到initProxy代碼中
if (hasProxy) { ... vm._renderProxy = new Proxy(vm, handlers) } else { vm._renderProxy = vm } }
若是Proxy屬性存在,則把包裝後的vm屬性賦值給_renderProxy屬性值。不然把vm是實例自己賦值給_renderProxy屬性
The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).
代理對象是es6的新特性,它主要用來自定義對象一些基本操做(如查找,賦值,枚舉等)。
咱們上面講的這些代碼主要應用了lookup和enumeration部分的自定義能力。proxy是一個強大的特性,爲咱們提供了不少"元編程"能力。只不過目前規範尚未很完善,使用的時候要稍加註意。最後按照咱們的老規矩,以一張圖完成今天的講解。