前几篇文章中,咱们主要讲了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是一个强大的特性,为咱们提供了不少"元编程"能力。只不过目前规范尚未很完善,使用的时候要稍加注意。最后按照咱们的老规矩,以一张图完成今天的讲解。