对象和属性
JS中,对象是键值对的集合。这些值是对象的属性,它可以是一个基本类型或对象,也可以是方法(method)。
比如:
const obj = {
name: 'Brynn',
sayName() {
console.log('Brynn!')
},
}
console.log(obj)
console.log(obj.sayName.name) // sayName
上面这个对象有一个name基本类型,和一个greet方法。
字面量定义属性
用上面这种定义方法是最简单的,称为用字面量定义。 将对象的属性和方法直接写在对象内。
const obj = {
name: 'Brynn',
sayName() {
console.log('Brynn!')
},
greet: function () {
console.log('Hello!')
},
}
console.log(obj.sayName.name) // sayName
console.log(obj.greet.name) // greet
其中,定义方法时,有两种写法。这两种写法的函数都是有名字的。
defineProperty配置属性
除此之外,还可以用Object.defineProperty()来配置一个属性,用Object.getOwnPropertyDescripter()查询配置具体情况。
const obj = {}
Object.defineProperty(obj, 'name', {
value: 'Brynn',
writable: true,
enumerable: true,
configurable: true,
})
Object.defineProperty(obj, 'sayName', {
value: function sayName() {
console.log('Brynn!')
},
writable: true,
enumerable: true,
configurable: true,
})
console.log(obj)
console.log(obj.sayName.name) // sayName
propertyDescriptor
在Object.defineProperty中出现了一些属性(property)的特性(attribute),这些特性如下:
- value: 属性的值
- writable: 是否可修改属性的value
- set: 写入属性时调用的函数(使用Brynn.name = 'Boolean'就会调用set函数)
- get:获取属性时调用的函数(使用console.log(Brynn.name)就会调用get函数)
- configurable:是否可修改属性的attribute,以及只有可配置属性的才能被delete删除
- enumerable: 是否可枚举
可枚举的属性才能通过
Object.keys(target)以及for in/of获取,可枚举和不可枚举的都可以通过Object.getOwnPropertyNames(target)获取)
这些特性有默认值,
- 通过字面量定义的:
writable、configurable、enumerable都是true - 通过defineProperty配置的
writable、configurable、enumerable都是false
const obj = { name: 'Brynn' }
Object.defineProperty(obj, 'sayName', {
value: function sayName() {
console.log('Brynn!')
},
})
let descriptors = Object.getOwnPropertyDescriptors(obj)
console.log(descriptors) // {name:{value: "Brynn", writable: true, enumerable: true, configurable: true}, sayName: {writable: false, enumerable: false, configurable: false, value: ƒ}}
这些特性不能同时全部存在于一个属性上,根据这一点,属性又分为数据属性和访问器属性
- 数据属性:有
value/writable/enumerable/configurable四个特性 - 访问器属性: 有
set/get/enumerable/configurable四个特性
访问器属性和getter/setter(存值函数/取值函数)
访问器属性是对象属性的两个特性,setter/getter是在定义对象方法
使用访问器属性
const obj = {}
Object.defineProperty(obj, 'foo', {
get() {
return 'brynn'
},
set(x) {
this.foo = x
},
enumerable: true,
configurable: true,
})
let descriptors = Object.getOwnPropertyDescriptors(obj)
console.log(descriptors)
console.log(obj)
使用setter/getter
const obj = {
get foo() {
return 'brynn'
},
set foo(x) {
this.foo = x
},
}
let descriptors = Object.getOwnPropertyDescriptors(obj)
console.log(descriptors)
console.log(obj)
区别是
- 用defineProperty添加属性更灵活,可以动态添加
- 特性的默认值不同,defineProperty的默认值是false,setter/getter的默认值是true
作为原型
这两种方法构造出的对象作为另一个对象的原型,都会使得另一个对象在控制台输出时有对应的属性,但它并不是真正的属性(getOwnDescriptors没有)
const obj = {}
Object.defineProperty(obj, 'foo', {
get() {
return 'brynn'
},
set(x) {
this.foo = x
},
enumerable: true,
configurable: true,
})
let upObj = {
value: 'test',
}
upObj.__proto__ = obj
let descriptors = Object.getOwnPropertyDescriptors(upObj)
console.log(upObj)
console.log(descriptors)