属性的分类与获取

可枚举属性

由属性的内部特性[[Enumerable]]决定。它表示属性是否可以通过for-in循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是true。如:

  1. let obj = {name:"test",age:23}
  2. for(property in obj){
  3. console.log(property,obj[property]);
  4. }
  5. //name test
  6. //age 23

获取方式:

1. 用方括号或者点运算符

  1. obj.name; //直接用属性名
  2. obj['age']; //注意方括号内是字符串类型的属性名,可以是一个字符串变量

2. for-in 如上面的代码

for-in遍历的是对象的属性名,与for-of不一样,后者是遍历数组。

  1. let arr = ['javascript','css','html']
  2. for(let k in arr){
  3. console.log(k);
  4. }
  5. //0,1,2
  6. for(let v of arr){
  7. console.log(v);
  8. }
  9. //'javascript','css','html'

image.png
这跟一般对象的结构很相似,但是不能这样访问arr.0。

3. Object方法

  1. for(let k of Object.keys(obj)){
  2. console.log(k);
  3. }
  4. //name
  5. //age
  6. for(let v of Object.values(obj){
  7. console.log(v);
  8. }
  9. //test
  10. //123
  11. for(let [k,v] of Object.entries(obj)){
  12. console.log(k,v);
  13. }
  14. //name test
  15. //age 123

Object.keys(),Object.values(),Object.entries()返回的都是数组,所以要用数组解构。

4. 知道属性名可以进行解构获取属性值

5. Object.getOwnPropertyNames()

这个函数返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
于是可以迭代出所有的属性值。

  1. Object.defineProperties(obj, {
  2. 'name': {
  3. value: 'test',
  4. writable: true
  5. },
  6. 'age': {
  7. value:123,
  8. writable: false
  9. },
  10. 'sex': {
  11. value: 'male',
  12. enumerable:false
  13. },
  14. 'sayHello': {
  15. value: ()=>console.log('Hello'),
  16. writable: false,
  17. }
  18. });
  19. console.log(Object.getOwnPropertyNames(obj));
  20. //['name', 'age', 'sex', 'sayHello']

既然能获得键名,自然也能获取到属性值了。


不可枚举属性

需要用Object.defineProperty()或Object.defineProperties()去定义。

  1. let person = {};
  2. Object.defineProperty(person, "name", {
  3. enumerable:false,
  4. value: "Nicholas"
  5. });
  6. console.log(person.name); // "Nicholas"
  7. for( p in person){
  8. console.log(p,person[p]);
  9. }
  10. //无输出

那么如何获取不可枚举属性呢?

知道属性名

  1. person['name']
  2. person.name
  3. let {name} = person; //解构

不知道属性名:Object.getOwnPropertyNames()

  1. function getOwnNonEnumerableProperties(obj){
  2. if(!obj || typeof obj !== 'object') return null;
  3. //获取自身的不可枚举的属性
  4. let nonEnumerableKeys = Object.getOwnPropertyNames(obj);
  5. let result = [];
  6. for(let key of nonEnumerableKeys){
  7. let descriptor = Object.getOwnPropertyDescriptor(obj,key);
  8. if(descriptor && !descriptor.enumerable){
  9. let value = descriptor.value;
  10. result.push({key,value});
  11. }
  12. }
  13. return result;
  14. }
  15. o = {};
  16. Object.defineProperty(o, "baz", {
  17. value: 8675309,
  18. writable: false,
  19. enumerable: false
  20. });
  21. console.log(getOwnNonEnumerableProperties(o));
  22. //[ { key: 'baz', value: 8675309 } ]

继承属性

即属性不是实例本身定义的,而是从其原型链上获取的。

如何定义属性

Object.defineProperty()

  1. //Object.defineProperty()
  2. var o = {}; // 创建一个新对象
  3. // 在对象中添加一个属性与数据描述符的示例
  4. Object.defineProperty(o, "a", {
  5. value : 37,
  6. writable : true,
  7. enumerable : true,
  8. configurable : true
  9. });
  10. // 对象 o 拥有了属性 a,值为 37
  11. // 在对象中添加一个设置了存取描述符属性的示例
  12. var bValue = 38;
  13. Object.defineProperty(o, "b", {
  14. // 使用了方法名称缩写(ES2015 特性)
  15. // 下面两个缩写等价于:
  16. // get : function() { return bValue; },
  17. // set : function(newValue) { bValue = newValue; },
  18. get() { return bValue; },
  19. set(newValue) { bValue = newValue; },
  20. enumerable : true,
  21. configurable : true
  22. });
  23. o.b; // 38
  24. // 对象 o 拥有了属性 b,值为 38
  25. // 现在,除非重新定义 o.b,o.b 的值总是与 bValue 相同
  26. // 数据描述符和存取描述符不能混合使用
  27. Object.defineProperty(o, "conflict", {
  28. value: 0x9f91102,
  29. get() { return 0xdeadbeef; }
  30. });
  31. // 抛出错误 TypeError: value appears only in data descriptors, get appears only in accessor descriptors

更多实例

Object.defineProperties()

  1. let book = {};
  2. Object.defineProperties(book, {
  3. year_: {
  4. value: 2017
  5. },
  6. edition: {
  7. value: 1
  8. },
  9. year: {
  10. get() {
  11. return this.year_;
  12. },
  13. set(newValue) {
  14. if (newValue > 2017) {
  15. this.year_ = newValue;
  16. this.edition += newValue - 2017;
  17. }
  18. }
  19. }
  20. });

直接赋值

  1. obj.newProperty = "blue";
  2. obj['newPropertyplus'] = "tall"

获取属性的方法封装

获取所有属性名,自身的和原型链上的

  1. function getAllPropertyNames( obj ) {
  2. var props = [];
  3. do {
  4. Object.getOwnPropertyNames( obj ).forEach(function ( prop ) {
  5. if ( props.indexOf( prop ) === -1 ) {
  6. props.push( prop );
  7. }
  8. });
  9. } while ( obj = Object.getPrototypeOf( obj ) );
  10. return props;
  11. }
  12. console.log(getAllPropertyNames(['a','b','c']));
  13. //打印结果
  14. [
  15. '0', '1', '2',
  16. 'length', 'constructor', 'concat',
  17. 'copyWithin', 'fill', 'find',
  18. 'findIndex', 'lastIndexOf', 'pop',
  19. 'push', 'reverse', 'shift',
  20. 'unshift', 'slice', 'sort',
  21. 'splice', 'includes', 'indexOf',
  22. 'join', 'keys', 'entries',
  23. 'values', 'forEach', 'filter',
  24. 'flat', 'flatMap', 'map',
  25. 'every', 'some', 'reduce',
  26. 'reduceRight', 'toLocaleString', 'toString',
  27. 'at', '__defineGetter__', '__defineSetter__',
  28. 'hasOwnProperty', '__lookupGetter__', '__lookupSetter__',
  29. 'isPrototypeOf', 'propertyIsEnumerable', 'valueOf',
  30. '__proto__'
  31. ]

获取自身的不可枚举的属性

  1. function getOwnNonEnumerableProperties(obj){
  2. if(!obj || typeof obj !== 'object') return null;
  3. let nonEnumerableKeys = Object.getOwnPropertyNames(obj);
  4. let result = [];
  5. for(let key of nonEnumerableKeys){
  6. let descriptor = Object.getOwnPropertyDescriptor(obj,key);
  7. if(descriptor && !descriptor.enumerable){
  8. let value = descriptor.value;
  9. result.push({key,value});
  10. }
  11. }
  12. return result;
  13. }
  14. o = {};
  15. Object.defineProperty(o, "baz", {
  16. value: 8675309,
  17. writable: false,
  18. enumerable: false
  19. });
  20. console.log(getOwnNonEnumerableProperties(o));

获取自身的所有属性

  1. function getOwnAllProperties(obj){
  2. if(!obj || typeof obj !== 'object') return null;
  3. let nonEnumerableKeys = Object.getOwnPropertyNames(obj);
  4. let result = [];
  5. for(let key of nonEnumerableKeys){
  6. let descriptor = Object.getOwnPropertyDescriptor(obj,key);
  7. let value = descriptor.value;
  8. result.push({key,value});
  9. }
  10. return result;
  11. }
  12. console.log(getOwnAllProperties(['a','b','c']));
  13. //打印结果
  14. [
  15. { key: '0', value: 'a' },
  16. { key: '1', value: 'b' },
  17. { key: '2', value: 'c' },
  18. { key: 'length', value: 3 }
  19. ]

按内部特性分类

数据属性

含有的内部特性为:

  1. [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这特性都是 true。
  2. [[Enumerable]] :表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。
  3. [[Writable]] :表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
  4. [[Value]] :包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为 undefined 。

访问器属性

含有的内部特性为:

  1. [[Configurable]] :表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为数据属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。
  2. [[Enumerable]] :表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true 。
  3. [[Get]] :获取函数,在读取属性时调用。默认值为 undefined 。
  4. [[Set]] :设置函数,在写入属性时调用。默认值为 undefined 。

数据属性和访问器属性的内部属性不能混在一起,比如既设置了[[Value]]又设置[[Get]]。