这是我参与更文挑战的第17天,活动详情查看: 更文挑战
前言
我们知道JS对象的属性分类很多,有些是构造函数自有的,有些是原型链的,有些是可枚举的,有些又是不可改变的等等,ES6又加了一个Symbol类型,让JS对象属性更加丰富,这篇文章就是来捋一下JS对象的属性。
按来源分类
- 自有属性
- 原型属性
in操作符可以检查对象上所有的自有属性和原型属性,包括Symbol值。
hasOwnProperty方法可以判断某个属性是否是该对象的自有属性,包括Symbol值。
let s1 = Symbol('s1');
function Fun() {
this.innerValue = '自有属性';
this[s1] = 'Symbol属性'
}
Fun.prototype.proValue = "原型属性";
let obj = new Fun()
console.log('innerValue' in obj);
console.log('proValue' in obj);
console.log(s1 in obj);
console.log('-----------------');
console.log(obj.hasOwnProperty('innerValue'));
console.log(obj.hasOwnProperty(s1));
console.log(obj.hasOwnProperty('proValue'));
//执行结果
true
true
true
-----------------
true
true
false
属性枚举
in可以枚举对象上所有的可枚举的自有属性和原型属性(不包括Symbol值)。
Object.keys只枚举可以枚举的自有属性(不包括Symbol值)。
Object.getOwnPropertyNames返回一个指定对象所有的自身属性(包括不可枚举属性,但不包括Symbol值)的数组。
Object.getOwnPropertySymbols返回一个指定对象所有的Symbol值。
Reflect.ownKeys可以返回一个对象自身的所有属性,包括不可枚举属性和Symbol属性
function Fun() {
this.innerValue = '自有属性';
}
Fun.prototype.proValue = "原型属性";
let obj = new Fun();
Object.defineProperty(obj, 'noEnum', {
value: '不可枚举属性'
})
let s1 = Symbol('s1');
obj[s1] = 'symbol属性'
let forInArr = [];
for(let key in obj) {
forInArr.push(key);
}
console.log(forInArr);
console.log(Object.keys(obj));
console.log(Object.getOwnPropertyNames(obj));
console.log(Reflect.ownKeys(obj))
//执行结果
[ 'innerValue', 'proValue' ]
[ 'innerValue' ]
["innerValue", "noEnum"]
["innerValue", "noEnum", Symbol(s1)]
按作用分类
- 数据属性
- 访问器属性
数据属性
数据属性包含了一个值,对象的内部方法[[put]]的默认行为就是创建数据属性。数据属性拥有的特征属性:
[[Value]] 存储属性的值。
[[Writable]] 存储一个布尔值,表明该属性是否可以改变。
[[Enumerable]] 存储一个布尔值,表明该属性是否可枚举。
[[Configurable]] 存储一个布尔值,控制一个属性的元数据的可写性。若为false,则不能删除该属性;不能改变这个属性的大部分特性(除了[[Value]]和[[Writable]]可变);不能将一个数据属性重新定义为访问器属性。
let obj = {
//x,y为数据属性
x: 1,
y: 2
}
访问器属性
访问器属性不包含值,而是定义一个getter和setter函数。访问器属性拥有的特征属性:
[[Get]]存储getter函数,在读取该属性的值时会调用此函数,函数的返回值就是该属性的值。
[[Set]]存储setter函数,在给该属性赋值时会调用此函数。该函数在调用时会传入一个参数(赋的新值)。
[[Enumerable]] 存储一个布尔值,表明该属性是否可枚举。
[[Configurable]] 存储一个布尔值,控制一个属性的元数据的可写性。若为false,则不能删除该属性;不能改变这个属性的大部分特性(除了[[Value]]和[[Writable]]);不能将一个数据属性重新定义为访问器属性。
let obj = {
x:1,
y:2,
//z为访问器属性
get z() {
return 3;
},
set z(val) {
return 3;
}
}
obj.z = 5;
console.log(obj.z); //3
定义对象属性
使用Object.defineProperty定义对象属性时,数据属性和访问器属性特性的默认值:
特性名称 | 默认值 |
---|---|
[[Value]] | undefined |
[[Writable]] | false |
[[Get]] | undefined |
[[Set]] | undefined |
[[Enumerable]] | false |
[[Configurable]] | false |
用以下两种方式直接在对象上定义属性时,它们的[[Configurable]], [[Enumerable]]和[[Writable]]特性默认为true,[[Value]]为指定的值
//方式一:
let obj = {};
obj.val = 'xxxx';
//方式二:
let obj = {
val: 'xxxx'
}
属性描述符
可以利用属性描述符将一个属性的所有特性用一个对象返回
{
value: 1,
writable: true,
enumerable: true,
configurable: true
}
使用属性描述符的函数
1. Object.defineProperty(obj, propName, PropDesc)
2. Object.defineProperties(obj, propDescObj)
3. Object.create(proto, propDescObj)
4. Object.getOwnPropertyDescriptor(obj, propName) //返回对象obj的propName属性的自身属性(非集成)的属性描述符
5. Object.getOwnPropertyDescriptors(obj)
Object.freeze()
Object.freeze()会创建一个冻结对象。一旦对象被冻结,则不能再向里边添加新的属性;不能删除已有的属性;不能修改该对象已有属性的特征属性(可枚举,可配置,可写等);不能修改已有属性的值。
Object.isFrozen()方法用来判断一个对象是否被冻结。
Object.seal()
Object.seal()封闭一个对象。阻止添加新属性;将所有的现有属性标记为不可配置;当前属性如果原来是可写的,则可以改变。
Object.isSealed()方法用来判断一个对象是否被密封。
Object.preventExtensions()
Object.preventExtensions()让一个对象变得不可扩展,永远不能再添加新属性。(如果一个对象可以添加新的属性,则这个对象是扩展的。)一旦被标记为不可扩展的,则不能再变为可扩展;Object.preventExtensions()仅阻止添加自身属性,其对象原型不受影响;不可扩展的对象属性仍然可以删除。
Object.isExtensible()方法用来判断一个对象是否可扩展。
Object.freeze, Object.seal, Object.preventExtensions方法都返回原对象,不会创建副本。
特征属性的继承
数据属性[[Writable]],[[Value]],[[Enumerable]]是可继承的。访问器属性[[Get]],[[Set]][[Enumerable]]是可继承的。
//设置writable为false,则子对象无法修改
let obj1 = {
name:'obj1'
}
let obj2 = {}
Object.setPrototypeOf(obj2, obj1);
console.log(obj2.name); //obj1
console.log(Object.getOwnPropertyDescriptor(obj2, 'name')); //undefined
Object.defineProperty(obj1, 'name',{
writable: false
})
obj2.name = 'obj2'
console.log(obj2.name); //obj1
[[Configurable]]是不继承的
let test = {
age:12
}
Object.defineProperty(test, 'name', {
value: 'hie',
writable: true,
configurable: false,
enumerable: false
});
let test2 = Object.create(test);
test2.name = 'test2'
console.log("-------删除前", test2.name); //test2
delete test2.name //true,删除成功
console.log('------删除后', test2.name) //hie
delete test.name //false,无法删除
全局环境下的特殊情况:
a=1与var a=1
原因:全局环境下,a=1 和 var a=1 都是为window对象添加一个属性。不同的是前者跟字面量定义属性一样,configurable默认为true,所以可以delete,后者configurable默认为false,所以无法delete
内部属性
有一些属性仅仅是为规范所用的,他们无法通过Javascript直接访问到(需要通过特定的函数),但它们确实存在,这称为内部属性。内部属性的名称比较特殊,有两个中括号包围着。
- [[Prototype]]. 指向所属对象的原型。从ES6开始,[[Prototype]]可以通过Object.getPrototypeOf和Object.setPrototypeOf访问,这个等同于Javascript的非标准但许多浏览器实现的属性__proto__