当前位置: 首页 > 科技观察

为什么vue2这个可以直接获取数据和方法呢?

时间:2023-03-12 09:40:26 科技观察

在使用vue开发项目的时候,我们可能每天都会看到这样一段代码:constvm=newVue({data:{name:'我是pino',},methods:{print(){console.日志(this.name);}},});控制台日志(虚拟机名称);//我是pinovm.print();//我是pino,但是我们自己实现一个构造函数就不能达到这个效果?functionSuper(options){}constp=newSuper({data:{name:'pino'},methods:{print(){console.log(this.name);}}});console.log(p。姓名);//undefinedp.print();//p.p??rint不是函数那么这个调用方法在vue2中是如何实现的呢?源码可以先找到vue2的入口文件:src/core/instance/indexfunctionVue(options){if(process.env.NODE_ENV!=='production'&&!(thisinstanceofVue)){warn('Vue是一个构造函数,应该用`new`关键字调用')}this._init(options)}//初始化操作在这个函数中完成initMixin(Vue)stateMixin(Vue)eventsMixin(Vue)lifecycleMixin(Vue)renderMixin(vue)exportdefaultVue接下来看initMixin文件是如何实现的:exportfunctioninitMixin(Vue:Classreportanerrorprops存在,是否存在props同名属性->错误实例中是否存在同名属性,方法名保留->报错bindfunctionfunctionpolyfillBind(fn,ctx){functionboundFn(a){varl=arguments.length;//确定调用/应用调用的参数数量returnl?l>1?fn.apply(ctx,arguments):fn.call(ctx,a):fn.call(ctx)}boundFn._length=fn.length;returnboundFn}functionnativeBind(fn,ctx){returnfn.bind(ctx)}//判断是否支持nativebind方法varbind=Function.prototype.bind?nativeBind:polyfillBind;bind函数主要是为了兼容,如果不支持原来的bind函数,根据参数个数使用call/apply来绑定this。call/apply最大的区别就是传入参数的不同。另一个接受数组。hasOwn用于判断是否为对象自身拥有的对象。上面的函数是用来判断props中是否存在相同属性的://只判断是否为自己所有,不包括原型链搜索varhasOwnProperty=Object.prototype.有自己的财产;functionhasOwn(obj,key){returnhasOwnProperty.call(obj,key)}hasOwn({},'toString')//falsehasOwn({name:'pino'},'name')//trueisReserved判断是否保留命名内部私有(以$或_开头)函数isReserved(str){varc=(str+'').charCodeAt(0);返回c===0x24||c===0x5F}isReserved('_data');//trueisReserved('数据');//falseinitDatafunctioninitData(vm){vardata=vm.$options.data;//判断data是否为函数,如果是函数,则执行函数data=vmingetData._data=typeofdata==='function'?getData(数据,虚拟机):数据||{};//判断是否为对象if(!isPlainObject(data)){data={};warn('数据函数应该返回一个对象:\n'+'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',vm);}//在实例上代理数据//取值props/methods/data的值varkeys=Object.keys(data);变种道具=vm.$options.props;var方法=vm.$options.methods;vari=keys.length;//判断props/methods是否存在while(i--){varkey=keys[i];{if(methods&&hasOwn(methods,key)){warn(("Method\""+key+"\"已经被定义为数据属性。"),vm);}}if(props&&hasOwn(props,key)){warn("Thedataproperty\""+key+"\"isalreadydeclaredasaprop."+"Usepropdefaultvalueinstead.",vm);}elseif(!isReserved(key)){//代理拦截proxy(vm,"_data",key);}}//observedata//监控数据observe(data,true/*asRootData*/);}getData如果data是一个函数,调用这个函数执行data:functiongetData(data,vm){//#7573disable调用数据获取器pushTarget()时的dep集合;try{//将其绑定到实例returndata.call(vm,vm)}catch(e){handleError(e,vm,"data()");返回{}}最后{popTarget();}}proxy代理拦截,当使用this.xxx访问一个属性时,返回this.data.xxx//一个纯函数functionnoop(a,b,c){}//代理对象varsharedPropertyDefinition={enumerable:true,configurable:true,get:noop,set:noop};functionproxy(target,sourceKey,key){//获取拦截sharedPropertyDefinition.get=functionproxyGetter(){returnthis[sourceKey][key]};//设置拦截sharedPropertyDefinition.set=functionproxySetter(val){this[sourceKey][key]=val;};//使用Object.defineProperty拦截对象Object.defineProperty(target,key,sharedPropertyDefinition);}其实对数据的处理就是绑定数据中属性的key遍历到实例vm中,然后使用Object.defineProperty拦截,并将真正的数据操作转发给Object.defineProperty对象的属性值this.data:属性的默认值。writable:属性是否可写。enumerable:属性是否可以枚举。configurable:属性是否可以删除。set():该属性的更新操作调用的函数。get():获取属性值时调用的函数。简单实现functionPerson(options){letvm=thisvm.$options=optionsif(options.data){initData(vm)}if(options.methods){initMethods(vm,options.methods)}}functioninitData(vm){letdata=vm._data=vm.$options.dataletkeys=Object.keys(data)letlen=keys.lengthwhile(len--){letkey=keys[len]proxy(vm,"_data",key)}}varsharedPropertyDefinition={enumerable:true,configurable:true,get:noop,set:noop};functionproxy(target,sourceKeys,key){sharedPropertyDefinition.get=function(){returnthis[sourceKeys][key]}sharedPropertyDefinition.set=function(val){这个[sourceKeys][key]=val}Object.defineProperty(target,key,sharedPropertyDefinition)}functionnoop(a,b,c){}functioninitMethods(vm,methods){for(letkeyinmethods){vm[key]=typeof方法[key]==='功能'?methods[key].bind(vm):noop}}letp1=newPerson({data:{name:'pino',age:18},methods:{sayName(){console.log('我是'+this.name)}}})console.log(p1.name)//pinop1.sayName()//'我是pino'总结一下就可以回答题目的问题了:passthis直接访问的原因methods中的函数是因为methods中的方法通过bind将this指定为一个新的Vue实例(vm)。之所以this直接访问data中的数据,是因为data中的属性最终会被存储到新的Vue实例(vm)上的_data对象,访问this.xxx,在Object之后访问this._data.xxx。定义属性代理。