属性的分类与获取
可枚举属性
由属性的内部特性[[Enumerable]]决定。它表示属性是否可以通过for-in循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是true。如:
let obj = {name:"test",age:23}for(property in obj){console.log(property,obj[property]);}//name test//age 23
1. 用方括号或者点运算符
obj.name; //直接用属性名obj['age']; //注意方括号内是字符串类型的属性名,可以是一个字符串变量
2. for-in 如上面的代码
for-in遍历的是对象的属性名,与for-of不一样,后者是遍历数组。
let arr = ['javascript','css','html']for(let k in arr){console.log(k);}//0,1,2for(let v of arr){console.log(v);}//'javascript','css','html'

这跟一般对象的结构很相似,但是不能这样访问arr.0。
3. Object方法
for(let k of Object.keys(obj)){console.log(k);}//name//agefor(let v of Object.values(obj){console.log(v);}//test//123for(let [k,v] of Object.entries(obj)){console.log(k,v);}//name test//age 123
Object.keys(),Object.values(),Object.entries()返回的都是数组,所以要用数组解构。
4. 知道属性名可以进行解构获取属性值
5. Object.getOwnPropertyNames()
这个函数返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
于是可以迭代出所有的属性值。
Object.defineProperties(obj, {'name': {value: 'test',writable: true},'age': {value:123,writable: false},'sex': {value: 'male',enumerable:false},'sayHello': {value: ()=>console.log('Hello'),writable: false,}});console.log(Object.getOwnPropertyNames(obj));//['name', 'age', 'sex', 'sayHello']
既然能获得键名,自然也能获取到属性值了。
不可枚举属性
需要用Object.defineProperty()或Object.defineProperties()去定义。
let person = {};Object.defineProperty(person, "name", {enumerable:false,value: "Nicholas"});console.log(person.name); // "Nicholas"for( p in person){console.log(p,person[p]);}//无输出
知道属性名
person['name']person.namelet {name} = person; //解构
不知道属性名:Object.getOwnPropertyNames()
function getOwnNonEnumerableProperties(obj){if(!obj || typeof obj !== 'object') return null;//获取自身的不可枚举的属性let nonEnumerableKeys = Object.getOwnPropertyNames(obj);let result = [];for(let key of nonEnumerableKeys){let descriptor = Object.getOwnPropertyDescriptor(obj,key);if(descriptor && !descriptor.enumerable){let value = descriptor.value;result.push({key,value});}}return result;}o = {};Object.defineProperty(o, "baz", {value: 8675309,writable: false,enumerable: false});console.log(getOwnNonEnumerableProperties(o));//[ { key: 'baz', value: 8675309 } ]
继承属性
即属性不是实例本身定义的,而是从其原型链上获取的。
如何定义属性
Object.defineProperty()
//Object.defineProperty()var o = {}; // 创建一个新对象// 在对象中添加一个属性与数据描述符的示例Object.defineProperty(o, "a", {value : 37,writable : true,enumerable : true,configurable : true});// 对象 o 拥有了属性 a,值为 37// 在对象中添加一个设置了存取描述符属性的示例var bValue = 38;Object.defineProperty(o, "b", {// 使用了方法名称缩写(ES2015 特性)// 下面两个缩写等价于:// get : function() { return bValue; },// set : function(newValue) { bValue = newValue; },get() { return bValue; },set(newValue) { bValue = newValue; },enumerable : true,configurable : true});o.b; // 38// 对象 o 拥有了属性 b,值为 38// 现在,除非重新定义 o.b,o.b 的值总是与 bValue 相同// 数据描述符和存取描述符不能混合使用Object.defineProperty(o, "conflict", {value: 0x9f91102,get() { return 0xdeadbeef; }});// 抛出错误 TypeError: value appears only in data descriptors, get appears only in accessor descriptors
Object.defineProperties()
let book = {};Object.defineProperties(book, {year_: {value: 2017},edition: {value: 1},year: {get() {return this.year_;},set(newValue) {if (newValue > 2017) {this.year_ = newValue;this.edition += newValue - 2017;}}}});
直接赋值
obj.newProperty = "blue";obj['newPropertyplus'] = "tall"
获取属性的方法封装
获取所有属性名,自身的和原型链上的
function getAllPropertyNames( obj ) {var props = [];do {Object.getOwnPropertyNames( obj ).forEach(function ( prop ) {if ( props.indexOf( prop ) === -1 ) {props.push( prop );}});} while ( obj = Object.getPrototypeOf( obj ) );return props;}console.log(getAllPropertyNames(['a','b','c']));//打印结果['0', '1', '2','length', 'constructor', 'concat','copyWithin', 'fill', 'find','findIndex', 'lastIndexOf', 'pop','push', 'reverse', 'shift','unshift', 'slice', 'sort','splice', 'includes', 'indexOf','join', 'keys', 'entries','values', 'forEach', 'filter','flat', 'flatMap', 'map','every', 'some', 'reduce','reduceRight', 'toLocaleString', 'toString','at', '__defineGetter__', '__defineSetter__','hasOwnProperty', '__lookupGetter__', '__lookupSetter__','isPrototypeOf', 'propertyIsEnumerable', 'valueOf','__proto__']
获取自身的不可枚举的属性
function getOwnNonEnumerableProperties(obj){if(!obj || typeof obj !== 'object') return null;let nonEnumerableKeys = Object.getOwnPropertyNames(obj);let result = [];for(let key of nonEnumerableKeys){let descriptor = Object.getOwnPropertyDescriptor(obj,key);if(descriptor && !descriptor.enumerable){let value = descriptor.value;result.push({key,value});}}return result;}o = {};Object.defineProperty(o, "baz", {value: 8675309,writable: false,enumerable: false});console.log(getOwnNonEnumerableProperties(o));
获取自身的所有属性
function getOwnAllProperties(obj){if(!obj || typeof obj !== 'object') return null;let nonEnumerableKeys = Object.getOwnPropertyNames(obj);let result = [];for(let key of nonEnumerableKeys){let descriptor = Object.getOwnPropertyDescriptor(obj,key);let value = descriptor.value;result.push({key,value});}return result;}console.log(getOwnAllProperties(['a','b','c']));//打印结果[{ key: '0', value: 'a' },{ key: '1', value: 'b' },{ key: '2', value: 'c' },{ key: 'length', value: 3 }]
按内部特性分类
数据属性
含有的内部特性为:
[[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这特性都是 true。[[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。[[Writable]]:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。[[Value]]:包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为 undefined 。
访问器属性
含有的内部特性为:
[[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为数据属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。[[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。[[Get]]:获取函数,在读取属性时调用。默认值为 undefined 。[[Set]]:设置函数,在写入属性时调用。默认值为 undefined 。
数据属性和访问器属性的内部属性不能混在一起,比如既设置了[[Value]]又设置[[Get]]。
