原型链Getter/Setter的屏蔽效应
2021-11-28
| 2023-2-19
0  |  0 分钟
password
Created
Feb 19, 2023 04:45 PM
type
Post
status
Published
date
Nov 28, 2021
slug
summary
原型链Getter/Setter的屏蔽效应
tags
JavaScript
category
原理
icon
// 创建parent对象 拥有get/set以及_name const parent = { _name: null, get name() { return this._name }, set name(value) { this._name = value } } // 创建一个空对象child 相当于 child.__proto__ = parent 实现原型继承 const child = Object.create(parent) // 为child实例赋值name属性 child.name = '19Qingfeng' console.log(child, 'child')
按常理来讲, 我们为child添加了name属性, 名为19Qingfeng, 但实际上的结果是这样的
notion image
我们明明是在实例 child 上进行了赋值,可是为什么 child 上并没有出现所谓的 name 属性,而是拥有了一个名为 _name 的 19Qinfeng ?
在给child.name赋值的时候, 其机制是这样的:
  1. 在child中如果有name属性, name就会修改已有的属性值
  1. 如果name属性不存在于child上, 则会遍历child的prototype查找这个属性
  1. 如果没有这个属性, 则name的赋值会被直接添加到child的实例对象上
  1. 如果原型链中有这个属性
    1. 如果原型链上是普通的name属性, 那么在原型链上行的name会被child上新赋值的name屏蔽
    2. 如果原型链上是普通的name属性, 但name被定义了writable为false, 也就是不可写, 那么这个时候的name赋值是直接失效的
    3. const c = {a: 1} Object.defineProperty(c, 'a', { writable: false }) const d = Object.create(c) d.a = 2 console.log(d) // { } => { a: 1 } 没有任何变化
      c. 如果原型链上存在一个名为name的setter时, 会调用这个setter
      const g = { a: 1, b: 1 } Object.defineProperty(g, 'b', { get() { return this.a }, set(val) { this.a = val } }) const h = Object.create(g) h.b = 3 console.log(h) // { a: 3, b: 3 } => { a: 1, b: 3 } 此时b被赋值到了h自己的属性上, 且因为原型链的b的setter, 导致h上多了一个b: 3 // 另外一种写法 const e = { b: 1, set a(val) { this.b = val } } const f = Object.create(e) f.a = 2 console.log(f) // { b: 2 } => { b: 1 }
       

      为什么Proxy会和Reflect搭配使用

      下面看一段代码, 当我们不考虑Proxy的第三个参数receiver的时候, 我们调用obj.value, 实际上获得的是其原型链上的name值
      const parent = { name: '19Qingfeng', get value() { return this.name; }, }; const handler = { get(target, key, receiver) { return Reflect.get(target, key); // 这里相当于 return target[key] }, }; const proxy = new Proxy(parent, handler); const obj = { name: 'wang.haoyu', }; // 设置obj继承与parent的代理对象proxy Object.setPrototypeOf(obj, proxy); // log: false console.log(obj.value); // 19Qingfeng
      显然这样是不符合预期的, 我们预期的结果应该是obj上的name
      此时我们需要对Reflect指定第三个参数receiver, 从而保证了this的正确指向
      const parent = { name: '19Qingfeng', get value() { return this.name; }, }; const handler = { get(target, key, receiver) { - return Reflect.get(target, key); + return Reflect.get(target, key, receiver); }, }; const proxy = new Proxy(parent, handler); const obj = { name: 'wang.haoyu', }; // 设置obj继承与parent的代理对象proxy Object.setPrototypeOf(obj, proxy); // log: wang.haoyu console.log(obj.value);
       
       
       
原理
  • JavaScript
  • 关于Babel的那些事儿Vue中指令的实现机制
    目录