Object.defineProperty实现observer方法,传入一个obj对象返回一个obj对象,返回的obj.name打印出get方法调用

99 阅读1分钟

代码实现

function observer(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    const target = Array.isArray(obj) ? [...obj] : {...obj};

    Object.keys(obj).forEach((item) => {
        const value = target[item];

        if (value && typeof value === 'object') {
            target[item] = observer(value);
            return;
        }

        Object.defineProperty(target, item, {
            get() {
                console.log(value);
                return value;
            },
            set(val) {
                console.log('val :', val);
            }
        });
    });

    return target;
}

const test = {
    target: {age: 2},
    arr: [1, 2, 3],
    name: 'test',
    method: function () {}
};

const result = observer(test);
console.log('result', result);

console.log('result.name :', result.name);
console.log('result.target.age :', result.target.age);
console.log('result.arr[0] :', result.arr[0]);

执行结果

result {
  target: { age: [Getter/Setter] },
  arr: [ [Getter/Setter], [Getter/Setter], [Getter/Setter] ],
  name: [Getter/Setter],
  method: [Getter/Setter]
}
test
result.name : test
2
result.target.age : 2
1
result.arr[0] : 1

优化代码 解决循环引用问题

function observer(obj, caches = []) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    const hit = caches.filter((i) => i.original === obj)[0];

    if (hit) {
        // 解决循环引用问题
        return hit.copy;
    }

    const target = Array.isArray(obj) ? [...obj] : {...obj};

    caches.push({original: obj, copy: target}); // 解决循环引用问题

    Object.keys(obj).forEach((item) => {
        const value = target[item];

        if (value && typeof value === 'object') {
            target[item] = observer(value, caches);
            return;
        }

        Object.defineProperty(target, item, {
            get() {
                console.log(value);
                return value;
            },
            set(val) {
                console.log('val :', val);
            }
        });
    });

    return target;
}

const test = {
    target: {age: 2},
    arr: [1, 2, 3],
    name: 'test',
    method: function () {}
};

test.test = test; // 循环引用

const result = observer(test);
console.log('result', result);

console.log('result.test :', result.test);
console.log('result.name :', result.name);
console.log('result.target.age :', result.target.age);
console.log('result.arr[0] :', result.arr[0]);

// result <ref *1> {
//     target: { age: [Getter/Setter] },
//     arr: [ [Getter/Setter], [Getter/Setter], [Getter/Setter] ],
//     name: [Getter/Setter],
//     method: [Getter/Setter],
//     test: [Circular *1]
//   }
//   result.test : <ref *1> {
//     target: { age: [Getter/Setter] },
//     arr: [ [Getter/Setter], [Getter/Setter], [Getter/Setter] ],
//     name: [Getter/Setter],
//     method: [Getter/Setter],
//     test: [Circular *1]
//   }
//   test
//   result.name : test
//   2
//   result.target.age : 2
//   1
//   result.arr[0] : 1