Javascript属性遮蔽问题

先了解一下Object.defineProperty()方法

Object.defineProperty() 静态方法会直接在一个对象上定义一个新属性,或修改其现有属性,并返回此对象。

javascript 复制代码
//obj:要定义的对象
//prop:一个字符串或 Symbol,指定了要定义或修改的属性键。
//descriptor:要定义或修改的属性的描述符,换句话说是一个属性描述对象
    /*
        configurable:代表该属性是否能被删除,默认值为:false
        enumerable:代表该属性是否能被遍历,默认值为:true
        value:代表该属性的值,默认值为:undefined
        writable:代表该属性是否可以重写,默认值为false
        get:是一个函数,用作属性 getter 的函数,如果没有 getter 则为 undefined
        set:是一个函数,用作属性 setter 的函数,如果没有 setter 则为 undefined      
    */
    
//返回值:传入函数的对象,其指定的属性已被添加或修改。

Object.defineProperty(obj, prop, descriptor)


/*
    此外,Object.defineProperty() 使用 [[DefineOwnProperty]] 内部方法,
    而不是 [[Set]],因此即使属性已经存在,它也不会调用 setter。
*/
javascript 复制代码
const object1 = {};

Object.defineProperty(object1, 'property1', {
  value: 42,
  writable: false,
});

object1.property1 = 77;
// Throws an error in strict mode  因为writable属性为false,不可重写该属性,所以值还是42

console.log(object1.property1);
// 42

目的:

通过Object.defineProperty()方法遮蔽类中的实例属性,并进行显示控制与操作

应用举例

如下代码中:在实例化之前通过Object.defineProperty该方法给Person类的原型对象上添加了age属性且匹配了对应的getter和setter,此时输出年龄没有问题

javascript 复制代码
class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}
//测试数据
let value = 20;

// 使用defineProperty给Person原型添加age属性,并配置对应的get与set
Object.defineProperty(Person.prototype, 'age', {
  get() {
    return value
  },
  set(val) {
    value = val
  }
})

const p1 = new Person('张三', 18)
console.log(p1.age) //18
console.log(Person.prototype.age)//18

需要注意:

第一次new Person时会调用Person类的constructor方法,在该方法内部会为实例对象赋初始值,当执行 this.age = age 时,首先这是在为实例对象添加age属性且赋初始值,但是js引擎会现在实例自身查找是否有age属性,如果没有会去Person类的原型对象上查找age属性,因为在实例化之前通过Object.defineProperty该方法给Person类的原型对象上添加了age属性且匹配了对应的getter和setter,所以age属性对应的setter就会执行,也就是说age属性被添加在了Person类的原型对象中,并非是Person类的实例

继续实例化,并分别通过实例对象输出age属性

javascript 复制代码
const p1 = new Person('张三', 18);
const p2 = new Person('李四', 30);
console.log(p1.age) //30
console.log(p2.age) //30

发现p1.age和p2.age属性值都是一样,有一种第二次实例化时传递进去的age参数覆盖掉了p1.age属性

具体原因:

在实例化之前通过Object.defineProperty该方法给Person类的原型对象上添加了age属性且匹配了对应的getter和setter,之后在实例化时,为实例对象设置age属性时,其实都是操作的Person类原型对象上的age属性,所以后者会覆盖前者

交换顺序:

先实例化然后在通过Object.defineProperty该方法给Person类的原型对象上添加了age属性且匹配了对应的getter和setter

javascript 复制代码
"use strict";
class Person1 {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}
//先实例化对象
const p1 = new Person1("Tom", 20);
const p2 = new Person1("Tom", 40);

//测试数据
let value1 = 20;

//在通过Object.defineProperty方法为Person原型上添加age属性
Object.defineProperty(Person1.prototype, "age", {
    get() {
        return value1;
    },
    set(val) {
        value1 = val;
    },
    //控制是否可以被枚举和删除
    enumerable: true, 
    configurable: true,
});

//输出不同实例的age属性
console.log(p1.age); //20
console.log(p2.age); //40

此时每个实例都有age属性,且age属性对应的值都是正常的,互不影响,因为age属性是存在于实例对象自身而不是Person类的原型对象中

但是会有一点瑕疵:Object.defineProperty的作用就没发挥出来,并没有遮蔽实例自身的age属性

完美写法:

关键:访问器中的this指向Person类的实例对象,所以可以通过this来区分,分别为不同的实例对象赋值

javascript 复制代码
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}
Object.defineProperty(Person.prototype, "age", {
    //访问器中的this指向Person类的实例对象,也就可以区分开来了
    //访问器中不能写["age"],因为会造成死循环,导致栈溢出
    get() {
        return this["__age"];
    },
    set(val) {
        this["__age"] = val;
    },
});

const p1 = new Person("Tom", 20);
const p2 = new Person("Tom", 40);

console.log(p1);
console.log(p2);

如果在访问器中写成 this["age"] 这样,就会出现下面的问题

相关推荐
谢尔登1 分钟前
【React】使用 useContext + useReducer 实现一个轻量的状态管理库
前端·javascript·react.js
Bee.Bee.6 分钟前
vue3提供的hook和通常的函数有什么区别
前端·javascript·vue.js
VisuperviReborn15 分钟前
前端开发者的知识深度革命,从打牢基础开始
前端·javascript·架构
来自星星的猫教授30 分钟前
Vue 3.6前瞻:响应式性能革命与Vapor模式展望
前端·javascript·vue.js
涵信34 分钟前
第九节 高频代码题-实现Sleep函数(异步控制)
前端·javascript·typescript
爱学习的茄子1 小时前
【前端实战】三分钟掌握原生JS电影搜索应用,从此告别框架依赖
前端·javascript·深度学习
时光足迹1 小时前
电子书阅读器之章节拆分
前端·javascript·react.js
WILLF1 小时前
【JavaScript】原型与原型链
javascript
涵信1 小时前
第八节 工程化与高级特性-模块与命名空间的选择
前端·javascript·typescript
利刃大大2 小时前
【在线五子棋对战】五、前端扫盲:html && css && javascript && ajax && jquery && websocket
前端·javascript·html