您现在的位置是:首页 >技术杂谈 >object.defineproperty方法解析/自己实现一个object.defineProperty/Vue3中的Proxy解析和实现网站首页技术杂谈

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

屈小康 2024-09-12 00:01:02
简介object.defineproperty方法解析/自己实现一个object.defineProperty/Vue3中的Proxy解析和实现
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);
    }
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。