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

相关推荐
学不会•24 分钟前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS1 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜3 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点3 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow3 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o3 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
刚刚好ā4 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
yqcoder5 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
会发光的猪。6 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客6 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js