小tips:JS之for in、Object.keys()和Object.getOwnPropertyNames()的区别

for..in循环

使用for..in循环时,返回的是所有能够通过对象访问的、可枚举的属性,既包括存在于实例中的属性,也包括存在于原型中的实例。这里需要注意的是使用for-in返回的属性因各个浏览器厂商遵循的标准不一致导致对象属性遍历的顺序有可能不是当初构建时的顺序。

虽然for..in主要用于遍历对象的属性,但同样也可以用来遍历数组元素。

var arr = ['a', 'b', 'c', 'd'];

// 使用for..in
for (var i in arr) {
  console.log('索引:' + i + ',值:' + arr[i]);
}

// 使用for循环
for (var j = 0; j < arr.length; j++) {
  console.log('索引:' + j + ',值:' + arr[j]);
}

/* 两种方式都输出:
 * ----------------
 * 索引:0,值:a
 * 索引:1,值:b
 * 索引:2,值:c
 * 索引:3,值:d
 * ----------------
 */

如何查看一个属性的特性可以使用propertyIsEnumberable()Object.getOwnPropertyDescriptor()这两个方法。

var colors = ['red', 'green', 'blue'];
// 扩展Array.prototype
Array.prototype.demo = function () {};

for (var i in colors) {
  console.log(i); // 输出: 0 1 2 demo
}

// 查看原生的方法[[enumberable]]特征,这里以splice为例
Array.prototype.propertyIsEnumerable('splice'); // false
Object.getOwnPropertyDescriptor(Array.prototype, 'splice'); // {writable: true, enumerable: false, configurable: true}

// 查看 demo 属性的特性
Array.prototype.propertyIsEnumerable('demo'); // true
Object.getOwnPropertyDescriptor(Array.prototype, 'demo'); // {writable: true, enumerable: true, configurable: true}

从上面的示例代码中可以看出,我们添加的demo方法,默认是可以被for..in枚举出来的。如果想让其不被枚举,那么可以使用ES5的Object.defineProperty()来定义属性,此外如果浏览器版本不支持ES5的话,我们可以使用hasOwnProperty()方法在for..in代码块内将可枚举的属性过滤掉。

var colors = ['red', 'green', 'blue'];
Object.defineProperty(Array.prototype, 'demo', {
  enumerable: false,
  value: function() {}
});

Array.prototype.propertyIsEnumerable('demo'); // false
Object.getOwnPropertyDescriptor(Array.prototype, 'demo'); // {writable: false, enumerable: false, configurable: false}

for (var i in colors) {
  console.log(i); // 输出:0 1 2
}

// 或者使用 hasOwnProperty
var colors = ['red', 'green', 'blue'];
Array.prototype.demo = function() {};

// 安全使用hasOwnProperty方法
var hasOwn = Object.prototype.hasOwnProperty;
for (var i in colors) {
  if (hasOwn.call(colors, i)) {
    console.log(i); // 输出:0 1 2
  }
}

Object.keys()

Object.keys()用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回一个由属性名组成的数组。注意它同for..in一样不能保证属性按对象原来的顺序输出。

// 遍历数组
var colors = ['red', 'green', 'blue'];
colors.length = 10;
colors.push('yellow');
Array.prototype.demo = function () {};

Object.keys(colors); // 0 1 2 10

// 遍历对象
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.demo = function() {};

var jenemy = new Person('jenemy', 25);

Object.keys(jenemy); // name age

注意在 ES5 环境,如果传入的参数不是一个对象,而是一个字符串,那么它会报 TypeError。在 ES6 环境,如果传入的是一个非对象参数,内部会对参数作一次强制对象转换,如果转换不成功会抛出 TypeError。

// 在 ES5 环境
Object.keys('foo'); // TypeError: "foo" is not an object

// 在 ES6 环境
Object.keys('foo'); // ["0", "1", "2"]

// 传入 null 对象
Object.keys(null); // Uncaught TypeError: Cannot convert undefined or null to object

// 传入 undefined
Object.keys(undefined); // Uncaught TypeError: Cannot convert undefined or null to object

由于Object.keys()为ES5上的方法,因此对于ES5以下的环境需要进行polyfill

// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
  Object.keys = (function() {
    'use strict';
    var hasOwn = Object.prototype.hasOwnProperty,
        hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
        dontEnums = [
          'toString',
          'toLocaleString',
          'valueOf',
          'hasOwnProperty',
          'isPrototypeOf',
          'propertyIsEnumerable',
          'constructor'
        ],
        dontEnumsLength = dontEnums.length;

      return function(obj) {
        if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
          throw new TypeError('Object.keys called on non-object');
        }

        var result = [], prop, i;

        for (prop in obj) {
          if (hasOwn.call(obj, prop)) {
            result.push(prop);
          }
        }

        if (hasDontEnumBug) {
          for (i = 0; i < dontEnumsLength; i++) {
            if (hasOwn.call(obj, dontEnums[i])) {
              result.push(dontEnums[i]);
            }
          }
        }
        return result;
      }
  }) ();
}

Object.getOwnPropertyNames()

Object.getOwnPropertyNames()方法返回对象的所有自身属性的属性名(包括不可枚举的属性)组成的数组,但不会获取原型链上的属性。

function A(a,aa) {
  this.a = a;
  this.aa = aa;
  this.getA = function() {
    return this.a;
  }
}
// 原型方法
A.prototype.aaa = function () {};

var B = new A('b', 'bb');
B.myMethodA = function() {};
// 不可枚举方法
Object.defineProperty(B, 'myMethodB', {
  enumerable: false,
  value: function() {}
});

Object.getOwnPropertyNames(B); // ["a", "aa", "getA", "myMethodA", "myMethodB"]

参考地址:https://www.cnblogs.com/wujie520303/p/4931384.html

posted @ 2018-03-27 14:33  风雨后见彩虹  阅读(1327)  评论(0编辑  收藏  举报