Object.defineProperty可以劫持数组吗?

·  阅读 282

这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

响应式系统是Vue中熟为人知的一个重要特性,其中存在一些问题也是被开发者们津津乐道的,比如对于数组的特殊处理就是一个典型问题,很多人在被问到为何Vue中需要对数组进行特殊处理时,部分人的回答既然是:Object.defineProperty只能劫持对象,不能接触数组,所以需要特殊处理数组

在揭晓答案之前,我们先来看一看简易版本的响应式实现

Object.defineProperty

总所周知的是:在Vue中响应式的核心之一便是Object.defineProperty,该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

语法:

Object.defineProperty(obj, prop, descriptor)
复制代码
  • obj:要定义属性的对象
  • prop:要定义或修改的属性的名称或Symbo
  • descriptor:要定义或修改的属性描述符

数据劫持

接下来直接上代码,核心代码已注释

/**
 * 数据劫持
 */
const defineReactive = (data, key, value) => {
    // 递归劫持数据
    observe(value);

    Object.defineProperty(data, key, {
        get() {
            console.log("get");
            return value;
        },
        set(newValue) {
            console.log("set");
            // 若是数据没有变化 则不处理
            if (newValue === value) return;
            // 递归劫持数据: 若是新赋值的数据是对象 需要继续劫持
            observe(newValue);
            value = newValue;
        },
    });
};

/** 
 * 用于处理数据
*/
const observe = (data) => {
    // 不是对象 不需要处理
    if (typeof data !== "object" || !data) {
        return data;
    }

     // 遍历对象
    Object.keys(data).forEach((key) => {
        defineReactive(data, key, data[key]);
    });
};
复制代码

接下来定义需要劫持的数据

const obj = {
    arr: [1, 2, 3],
    name: "nordon",
    info: {
        age: 12,
    },
};
复制代码

将数据劫持

observe(obj);
复制代码

首先验证一下对于常规数据的劫持效果

obj.name;
obj.name = "wy";
obj.info.age = 18
复制代码

此时控制台会依次输出:get、set、get、set

接下来看一下对于数组是否具有劫持效果

obj.arr.unshift(4);
复制代码

上述代码改变了arr的长度,在其首部增加一位数据,而且会触发多次get、set,证明使用Object.defineProperty是可以对数组进行数据劫持的,因此对于Vue中对于数组的特殊处理,并不是因为Object.defineProperty不能劫持数组,而是出于性能的考虑重写了7个数组的方法。

分类:
前端
标签:
分类:
前端
标签: