JavaScript 对象属性的声明

236 阅读4分钟

在JavaScript中,对象是一组属性的集合,其中成员既可以是普通的属性,也可以是方法,甚至可以是回调的事件。

声明一个对象,本质上就是为它的属性描述清楚以下五种性质:

  • Name:名字

  • Value:值

  • Writable:可写

  • Enumerable:可枚举

  • Configurable:可配置

在我们声明的时候,JavaScript 就已经默认配置了「Writable、Enumerable、Configurable」三种属性,所以我们一般情况下只需要指定 Name 和 Value 即可。

我们在使用对象字面量的时候,其实用的是名称的方式,快速定义了上述属性的一个简化版,代码如下:

obj = {
    name: 'value'
}

对象属性分为两种描述:数据属性和访问器属性,我们来分别阐述下。

数据属性

以上面声明的代码 obj.name 举例,它的描述符如下:

{
    value: 'value',
    writable: true,
    enumerable: true,
    configurable: true
}

对于有确切数据值的数据,我们称为数据属性。

访问器属性

首先是属性描述符和访问器属性不能同时存在,只能取其一。其次它的格式也是有区别的:

{
    get: function(){...},
    set: function(){...},
    enumerable: true,
    configurable: true,
}

在访问器属性中,wirtable 用 get、set 去代替了。

字面量风格声明

我们使用字面量风格的声明,不存在对 Enumerable 和 Configurable 的定义,因为他们总是使用默认值,为了兼容旧的对象声明语法,Writable 值需要按照语法分析来确定。

其一、当使用旧的字面量风格声明属性或添加对象属性时,JavaScript 总是会初始化一个数据属性,并包括两个性质:Value 和 Writable,其中 Writable 为 true。

//字面量风格声明
obj = {
    name: 'value'
}
//添加属性
obj.newName = 'value'

其二、 方法的声明可以视为上述传统声明的补充形式,在属性描述符的使用上与上述规则是一致的。

//方法声明
obj = {
    foo(){...}
}
//添加属性
obj.newName = 'value'

其三、 在使用存取方法来声明属性的时候,会初始化一个访问器属性,它包括两个函数 get、set 之一,或者两个都有。

//字面量风格声明「存取属性」
obj = {
    get name(){
        return 'value'
    }
}

在使用类声明时,属性描述符创建规则与上述一致。不过,类静态成员声明为类的属性,而一般成员声明为类的原型的属性。

对象属性赋值

在 JavaScript 中,对象属性的赋值操作会根据属性的存在情况、属性的特性和继承关系等不同情况做出不同的处理。具体来说:

一、如果属性不存在,将会隐式创建一个数据属性,其特性为可写、可枚举和可配置(也就是 Writable、Enumerable 和 Configurable 均为 true),并将赋值操作的值填入 value 中。

二、 如果这个属性已经存在,并且是数据属性,那么赋值操作就变成了更新。在这种情况下呢,只要 Writable 不是 false,也就是允许编辑,那么值就会被写入 Value 中,否则就什么也不做。

三、如果属性是继承来的数据属性,将在当前对象上创建一个新的数据属性,其特性为可写、可枚举和可配置。

这个新的数据属性本身不会继承任何「可枚举、可配置」的性质,而是会按照一个默认的数据描述方式来初始化,即:Writable、Enumerable 和 Configurable 均为 true。

四、 如果一个属性用的是访问器属性,都不会创建新的属性描述符。如果子类中继承了这样一个属性,那么在子类中对该属性的读写,也会忠实地调用继承而来的、原型中的读写器。

备注:

即使是全局属性,也会挂载在 window 对象上,属性描述也遵循上述规则。

数据属性和访问器属性在继承属性时的初始化逻辑是不同的。


题图生成:Midjourney
内容优化:ChatGPT
内容来源:《JavaScript 语言精髓与编程实战》

如果您对本篇文章中提到的问题有任何疑问或想法,请在评论区留言,我将尽力回复。

微信公众号「小道研究」,获取更多关于前端技术的深入分析和实践经验。