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

相关推荐
逆旅行天涯3 分钟前
【Threejs】从零开始(六)--GUI调试开发3D效果
前端·javascript·3d
长风清留扬44 分钟前
小程序毕业设计-音乐播放器+源码(可播放)下载即用
javascript·小程序·毕业设计·课程设计·毕设·音乐播放器
m0_748247801 小时前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter
ZJ_.1 小时前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
joan_852 小时前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
还是大剑师兰特2 小时前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
Watermelo6173 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
一个处女座的程序猿O(∩_∩)O5 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
燃先生._.11 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js