JS对象属性总结

1,221 阅读5分钟

这是我参与更文挑战的第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

image.png

原因:全局环境下,a=1 和 var a=1 都是为window对象添加一个属性。不同的是前者跟字面量定义属性一样,configurable默认为true,所以可以delete,后者configurable默认为false,所以无法delete

内部属性

有一些属性仅仅是为规范所用的,他们无法通过Javascript直接访问到(需要通过特定的函数),但它们确实存在,这称为内部属性。内部属性的名称比较特殊,有两个中括号包围着。

  • [[Prototype]]. 指向所属对象的原型。从ES6开始,[[Prototype]]可以通过Object.getPrototypeOf和Object.setPrototypeOf访问,这个等同于Javascript的非标准但许多浏览器实现的属性__proto__