本文首发于我的个人博客 ruiqima.com
原文链接:JS Properties
[[Configurable]]
:表示能否通过delete删除属性,能否修改属性特性,能否把属性修改为访问器属性。
[[Enumerable]]
:表示能否通过for-in循环返回属性。
[[Writable]]
:表示是否能修改属性值。
[[Value]]
:包含这个属性的数据值,默认值为undefined
。
当直接在对象上定义属性时(即var Person={name:'Joker'}
),前三个属性都默认为true
。
修改属性默认的特性的方法——Object.defineProperty()
在调用Object.defineProperty()方法创建一个新的属性时,如果不指定considerable
,innumerable
和writeable
特性的默认值都是false
。当只是修改一个已定义的属性的特性时,无此限制。
一旦把属性的considerable
定义为false
,即,把属性定义为不可配置的,则不能变回可配置了,且修改除writable
之外的属性都会发生错误。
var Person = {}
Object.defineProperty(person, 'name', {
value: 'Joker', //如果不指定那三个属性,则都默认为false
})
// 'use strict' //声明为严格模式
person.name = 'rewrite' // 在严格模式下会报错,在非严格模式下下会忽略该语句
在非严格模式下,修改已设置writable
为false
的属性的值,赋值操作将会被忽略;在严格模式下,将抛出错误。
var person = {
name: 'defaultname',
}
Object.defineProperty(person, 'name', {
writable: false, //设置name属性为不可修改
})
person.name = 'rewrite1' //重写失败,在非严格模式下会忽略,严格模式下会报错
console.log(person1.name) //defaultname
Object.defineProperty(person1, 'name', {
writable: true, //重新设置name属性为可修改
})
person1.name = 'rewrite2' //可以修改
console.log(person1.name) //rewrite2
包含getter
和setter
函数。在读取访问器属性时,会调用get
函数负责返回有效的值;在写入访问期属性时会调用set
函数并传入新值,负责如何处理数据。
通过Object.defineProperty()
来定义。
使用访问器属性的常见方式是,设置一个属性的值会导致其他属性发生变化,如下:
var book = {
year: 2000,
edition: 1,
}
Object.defineProperty(book, 'year', {
get: function () {
return this.year
},
set: function (newValue) {
if (newValue >= 2000) { // 改变year的同时改变edition
this.edition = Math.floor((newValue - 2000) / 3) + 1
}
},
})
book.year = 2001
console.log(book.edition) // 1
book.year = 2010
console.log(book.edition) // 4
只指定getter
意味着属性不可写,非严格模式下,尝试写入会被忽略,严格模式下,会抛出错误。
只指定setter
意味着属性不可读,非严格模式下,会返回undefined
,严格模式下,会抛出错误。
var person = {}
Object.defineProperty(person, 'name', {
enumerable: false,
value: 'name',
})
// 读取属性的特性
var descriptor = Object.getOwnPropertyDescriptor(person, 'name')
console.log(descriptor.enumerable) //false
console.log(descriptor.value) //name
.hasOwnProperty()
用于检测一个属性存在于实例中,还是在原型中。当存在于实例中时返回true。
function Person() {}
Person.prototype.name = 'defaultName' //添加一个原型属性
person.job = 'instanceJob' //添加一个实例属性
console.log(person.hasOwnProperty('job')) // true
console.log(person.hasOwnProperty('name')) // false
in操作符
:检测属性是否在实例或原型中,只要包括,就为true。
console.log('name' in person1) // true
console.log('job' in person1) // true
结合in操作符和hasOwnProperty()可以判断一个属性是实例属性还是原型属性
function hasPrototypeProperty(obj, name) {
return !obj.hasOwnProperty(name) && name in obj
}
console.log(hasPrototypeProperty(person, 'name')) // true
console.log(hasPrototypeProperty(person, 'job')) // false
// 现在person实例有实例属性name、sayName,原型属性job、sayJob
for (let prop in person1) {
console.log(prop) //所有实例属性、原型属性
}
注意let prop in person1
中的prop的类型是String
,因此如果要通过prop
访问person1
中的属性的值,应使用person1[pop]
,而不能使用person1.prop
。
访问js对象属性值[]
和.
的区别:[]
中接受的是字符串,如person1["name"]
,而.
接受的是直接的属性名,如person1.name
。
被设置为不可枚举,即emunerable为false的属性在for…in中不会被访问到。
function Person() {}
//定义原型属性,其中一个在后面被设置为不可枚举
Person.prototype.protoNameEmun = 'protoNameEmun'
Person.prototype.protoNameUnemun = 'protoNameUnemun'
Person.prototype.protoSayName = function () {}
var person = new Person()
//定义实例属性,其中一个在后面被设置为不可枚举
person.instanceNameEmun = 'instanceNameEmun'
person.instanceNameUnemun = 'instanceNameUnemun'
person.instanceSayName = function () {}
// 定义为不可枚举
Object.defineProperties(person, {
protoNameUnemun: {
enumerable: false,
},
instanceNameUnemun: {
enumerable: false,
},
})
// 输出实例属性、原型属性中所有可枚举的,不可枚举的不输出
// 输出:instanceNameEmun、instanceSayName、protoNameEmun、protoSayName
for (let prop in person) {
console.log(prop)
}
Object.keys()
方法这个方法接收一个对象作为参数,返回一个包含所有可枚举的实例属性的字符串数组。
同样,对于上述的例子:
var key1 = Object.keys(Person.prototype)
var key2 = Object.keys(person)
console.log(key1) //[ 'protoNameEmun', 'protoNameUnemun', 'protoSayName' ]
console.log(key2) //[ 'instanceNameEmun', 'instanceSayName' ]
返回包含所有实例属性(不论是否可以枚举)的字符串数组。
var names1 = Object.getOwnPropertyNames(Person.prototype)
var names2 = Object.getOwnPropertyNames(person)
console.log(names1)
// [ 'constructor', 'protoNameEmun', 'protoNameUnemun', 'protoSayName' ]
console.log(names2)
// [ 'instanceNameEmun', 'instanceNameUnemun', 'instanceSayName', 'protoNameUnemun' ]
注意在name1中包含了不可枚举属性constructor,正说明了“不论是否可以枚举”这一点。
在vscode中console.log(obj)出来的对象,显示的只有实例属性。
假设SuperType
中包括实例属性和方法name, sayName
,包括原型方法sayProto
,则:
// 自定义一个能打印出所有实例属性、原型属性的方法,使用for..in
function printAllProps(obj) {
let array = []
for (let prop in obj) {
array.push(prop)
}
console.log(array)
}
var obj = new SuperType('Joker')
console.log(obj)
printAllProps(obj)
最后两行在vscode中打印出来的内容分别为:
SuperType { name: 'Joker', sayName: [Function] }
[ 'name', 'sayName', 'sayProto' ]
可以看到,原型上的sayProto
方法并没有在console.log
中打印出来。
本文首发于我的个人博客ruiqima.com。
原文链接:https://www.ruiqima.com/zh/post/js-properties/
本博客内文章除特别声明外均为原创,采用CC BY-NC-SA 4.0 许可协议进行许可。超出CC BY-NC-SA 4.0 许可协议的使用请联系作者获得授权。