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"] 这样,就会出现下面的问题

相关推荐
伟笑28 分钟前
elementUI 循环出来的表单,怎么做表单校验?
前端·javascript·elementui
确实菜,真的爱1 小时前
electron进程通信
前端·javascript·electron
魔术师ID2 小时前
vue 指令
前端·javascript·vue.js
Clown953 小时前
Go语言爬虫系列教程 实战项目JS逆向实现CSDN文章导出教程
javascript·爬虫·golang
星空寻流年3 小时前
css3基于伸缩盒模型生成一个小案例
javascript·css·css3
waterHBO5 小时前
直接从图片生成 html
前端·javascript·html
EndingCoder5 小时前
JavaScript 时间转换:从 HH:mm:ss 到十进制小时及反向转换
javascript
互联网搬砖老肖5 小时前
React组件(一):生命周期
前端·javascript·react.js
HCl+NaOH=NaCl+H_2O6 小时前
Quasar组件 Carousel走马灯
javascript·vue.js·ecmascript
℘团子এ6 小时前
vue3中预览Excel文件
前端·javascript