上篇文章中,主要講了mergeOptions方法的處理邏輯,不明白的同窗們能夠點擊這裏查看。今天回到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,來執行不一樣的處理邏輯,咱們先來看hasProxy的源碼。函數
const hasProxy =
typeof Proxy !== 'undefined' &&
Proxy.toString().match(/native code/)
複製代碼
從變量的名字咱們知道它的做用就是判斷當前環境中Proxy是否可用,同窗們若是不熟悉Proxy的用法,能夠點擊這裏。這個判斷方法在咱們平時寫代碼中也能夠進行借鑑。post
回到代碼中,若是當前環境存在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相關的內容可點擊這裏。spa
接下來看看getHandler和hasHandler方法代理
const getHandler = {
get (target, key) {
if (typeof key === 'string' && !(key in target)) {
warnNonPresent(target, key)
}
return target[key]
}
}
複製代碼
getHandler方法主要是針對讀取代理對象的某個屬性時進行的操做。當訪問的屬性不是string類型或者屬性值在被代理的對象上不存在,則拋出錯誤提示,不然就返回該屬性值。code
該方法能夠在開發者錯誤的調用vm屬性時,提供提示做用。regexp
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實例上存在
const has = key in target
複製代碼
接下來經過下列語句,肯定屬性名稱是否可用
const isAllowed = allowedGlobals(key) || key.charAt(0) === '_'
複製代碼
allowedGlobals的定義以下:
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
......
}
複製代碼
這個方法咱們在平常寫代碼的時候也能夠進行借鑑。結合上面的分析,咱們知道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是一個強大的特性,爲咱們提供了不少"元編程"能力。只不過目前規範尚未很完善,使用的時候要稍加註意。最後按照老規矩,以一張圖完成今天的講解。