object.defineproperty方法解析/自己实现一个object.defineProperty/Vue3中的Proxy解析和实现

21 篇文章 3 订阅
7 篇文章 0 订阅
1、object.defineproperty

       首先说明一下 object.defineproperty不是Vue的方法,而是ES5中新增的方法。该方法可以用于定义对象属性的特性,包括可写性、可枚举、可配置性等。

该方法的代码如下:

Object.defineProperty(obj, prop, descriptor)

其中,obj表示要操作的目标对象,prop要定义或修改的属性名,descriptor是一个描述符对象,用来指定属性的特性。descriptor对象可以包含一下属性:

  • value:设置属性值,默认为undefined。
  • writable:设置属性是否可写(即是否可以被赋值),默认为false。
  • enumerable:设置属性是否可枚举(即是否可以通过for...in遍历循环得到),默认为false。
  • configurable:设置是否可配置(即是否可以使用delete属性删除或重新定义),默认为false。
  • get:定义属性的读取器函数,该函数会在读取属性值时被调用。
  • set:定义属性的设置器函数,该函数会在设置属性值时被调用。

比如如下我们创建一个对象:

const obj = {};
Object.defineProperty(obj, 'PI', {
  value: 3.14,
  writable: false,
  enumerable: true,
  configurable: false
});

console.log(obj.PI); // 输出 3.14
obj.PI = 3.14159;     // 不允许修改
console.log(obj.PI); // 输出 3.14

for (const key in obj) {
  console.log(key); // 输出 'PI'
}

delete obj.PI;       // 不允许删除
console.log(obj.PI); // 输出 3.14

需要注意的是,Object.defineProperty() 方法只能定义或修改单个属性。这也就是说如果有多个的话,必然在Vue的源码中有一个循环遍历。一下为一段源码看一下即可 循环为while循环

  function initData(vm) {
      var data = vm.$options.data;
      data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
      var keys = Object.keys(data);
      var props = vm.$options.props;
      var methods = vm.$options.methods;
      var i = keys.length;
//   --------------------------------------在这 在这--------------------------------
      while (i--) {
          var key = keys[i];
          {
              if (methods && hasOwn(methods, key)) {
                  warn$2("Method \"".concat(key, "\" has already been defined as a data property."), vm);
              }
          }
          if (props && hasOwn(props, key)) {
              warn$2("The data property \"".concat(key, "\" is already declared as a prop. ") +
                      "Use prop default value instead.", vm);
          }
          else if (!isReserved(key)) {
              proxy(vm, "_data", key);
          }
      }
      // observe data
      var ob = observe(data);
      ob && ob.vmCount++;
  }
  // 给大家稍微解释一下这段源码内容
  0、 data = vm._data = isFunction(data) ? getData(data, vm) : data || {};
      判断传过来的是 对象还是 函数 因为我们data本身有
      data:{}data(){return{}}两种方法!
      
  1、isReserved(key) 用于判断当前字段是否为 _ 或者 $ 开头,以 _ 或 $ 开头的属性 
  不会 被 Vue 实例代理,因为它们可能和 Vue 内置的属性、API 方法冲突。
  可以理解为只可读不可用,是vue源码中自己使用的。
  2proxy(vm, "_data", key);
  function proxy(target, sourceKey, key) {
      sharedPropertyDefinition.get = function proxyGetter() {
          return this[sourceKey][key];
      };
      sharedPropertyDefinition.set = function proxySetter(val) {
          this[sourceKey][key] = val;
      };
      Object.defineProperty(target, key, sharedPropertyDefinition);
  }
  可以看出是进行了代理。简单了解一下,如果想学可以去看看。

那我们怎么按照Vue的思想自己实现一个数据劫持那?

  • 明确方向:Vue本身通过的是 new 进行了实例化。
  • 属性:含有一个内部可操作属性this._data属性。
  • 添加方法:自定义 get set 方法。
  • 遍历:循环遍历添加属性。
let vm = new Vue({
	data:{
	name:"屈小康"
  }
}) 
// 定义class
class Vue{
// 为什么叫 options 因为源码就是这样写的
 constructor(options){
 	this._data = options.data;
 	this.$options = options;
 	this.initData();
 }
 initData(){
 	let data = this._data;
 	// 只针对于单层 data:{name:"屈小康" }
 	Object.keys(data).forEach(item=>{
 		Object.defineProperty(this,item,{
 		  enmuerable:true,
 		  configurable:true,
 		  get(){
 		  	return data[item];
 		  },
 		  set(value){
           	data[item] = value;    
           } 
  		})
    })
    
  }
}

那如果是 data:{name:"屈小康",person:{age:18}}怎么办? 显然我们需要递归的方法。

let vm = new Vue({
	data:{
	name:"屈小康"
  }
}) 
// 定义class
class Vue{
// 为什么叫 options 因为源码就是这样写的
 constructor(options){
 	this._data = options.data;
 	this.$options = options;
 	this.initData();
 }
 initData(){
 	let data = this._data;
    // 那如果是 data:{name:"屈小康",person:{age:18}}怎么办? 显然我们需要递归的方法。
    observe(data);
  }
}
// 添加 get 和 set 方法
function defineReactive(data,key,value){
	object(data[item]) //递归调用
	Object.defineProperty(data,key,{
		enumerable:trye,
		configurable:true,
		get(){
        	return value 
        },
		set(val){
       	data[key] = val;  
       }
    })
	
}
// 定义监听 class
class Observer{
	construct(data){
	   this.work(data)
    }
    work(data){
    	Object.keys(data).forEach(item=>{
			defineReactive(data,item,data[item]);
       })
    }
}
// 判断是否需要进行监听
function observe(data){
	    function observe(data) {
        let type = Object.prototype.toString.call(data);
        if (type !== '[object Object]' && type !== '[object Array]') {
            return;
        }
        new Observer(data);
    }
}
2、接下来介绍Proxy

    好首先我们先明确了解一下proxy,然后再和Vue进行关联。

Proxy是ES6中新增的一个对象,用于创建一个代理对象,可以拦截并定义基本操作的自定义行为。通过Proxy,我们可以对原始对象进行拦截和监控,从而实现一些高级的功能。等会我举个例子。
Proxy对象有两个参数,第一个参数是目标对象(即被代理的对象),第二个参数是处理器对象,里面定义了一些拦截的方法(也叫陷阱函数),用于拦截对目标对象的各种操作。

常见的拦截方法包括

target:原对象。
propKey:对象的话为key,数组的话为下标记。
receiver:被代理的对象,白话就是把target进行代理,对target进行修改过滤。(我感觉哈!这个参数可选可不选)

  • get(target, propKey,receiver):拦截对象属性的读取操作。
  • set(target, propKey,receiver):拦截对象属性的赋值操作。
  • apply(target, propKey,receiver):拦截函数的调用操作。
  • construct(target, propKey,receiver):拦截类的构造函数操作。
  • has(target, propKey,receiver):拦截in运算符操作。
  • deleteProxyperty(target, propKey,receiver):拦截delete操作。

举个例子吧,使用proxy实现一个对象的数据校验功能:

    const validator = {
        set(target, key, value) {
            if (key == 'age') {
                if (!Number.isInteger(value)) {
                    throw new TypeError('Age is not an integer')
                }
                if(value < 0){
                    throw new RangeError('Age must be a positive integer')
                }
            }
            target[key] = value;
            return true;
        }
    }

    const person = new Proxy({},validator);
    person.name = '屈小康';
    person.age = 18;
    console.log(person);// {name:'屈小康',age:18};
    //person.age = '不是数字'; //  throw new TypeError('Age is not an integer')
    //person.age = -1; // throw new RangeError('Age must be a positive integer')
    person.age = 10; 
    console.log(person)// {name:'屈小康',age:10};

在上面的示例中,我们使用 Proxy 对象创建了一个名为 person 的代理对象,通过 validator 处理器对象来拦截对 person 对象的赋值操作。当给 age 属性赋值时,会进行数据校验,并抛出相应的异常。

3、说一下Proxy在Vue3中的作用

    话不多说先介绍源码!
额目前还没看懂,再给我点时间。呜呜呜

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值