资讯专栏INFORMATION COLUMN

javascript中枚举 迭代对象的方法

MingjunYang / 2010人阅读

摘要:像和使用内置构造函数所创建的对象都会继承自和的不可枚举属性,例如的方法或的方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性更接近原型链中对象的属性覆盖原型属性。在中,非对象参数被强制转换为对象。

for...in

for...in 循环只遍历可枚举属性。像 Array和 Object使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性,例如 String 的 indexOf()方法或 Object的toString()方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性(更接近原型链中对象的属性覆盖原型属性)。
删除,添加或者修改属性
for...in 循环以任意序迭代一个对象的属性(请参阅delete运算符,了解为什么不能依赖于迭代的表面有序性,至少在跨浏览器设置中)。如果一个属性在一次迭代中被修改,在稍后被访问,其在循环中的值是其在稍后时间的值。一个在被访问之前已经被删除的属性将不会在之后被访问。在迭代进行时被添加到对象的属性,可能在之后的迭代被访问,也可能被忽略。

通常,在迭代过程中最好不要在对象上进行添加、修改或者删除属性的操作,除非是对当前正在被访问的属性。这里并不保证是否一个被添加的属性在迭代过程中会被访问到,不保证一个修改后的属性(除非是正在被访问的)会在修改前或者修改后被访问,不保证一个被删除的属性将会在它被删除之前被访问。

数组迭代和 for...in节

提示:for...in不应该用于迭代一个 Array,其中索引顺序很重要。

数组索引只是具有整数名称的枚举属性,并且与通用对象属性相同。不能保证for ... in将以任何特定的顺序返回索引。for ... in循环语句将返回所有可枚举属性,包括非整数类型的名称和继承的那些。

因为迭代的顺序是依赖于执行环境的,所以数组遍历不一定按次序访问元素。因此当迭代访问顺序很重要的数组时,最好用整数索引去进行for循环(或者使用 Array.prototype.forEach() 或 for...of 循环)。

仅迭代自身的属性节

如果你只要考虑对象本身的属性,而不是它的原型,那么使用 getOwnPropertyNames() 或执行 hasOwnProperty() 来确定某属性是否是对象本身的属性(也能使用propertyIsEnumerable)。或者,如果你知道不会有任何外部代码干扰,您可以使用检查方法扩展内置原型。

示例

下面的函数接受一个对象作为参数。被调用时迭代传入对象的所有可枚举属性然后返回一个所有属性名和其对应值的字符串。

var obj = {a:1, b:2, c:3};

for (var prop in obj) {
console.log("obj." + prop + " = " + obj[prop]);
}

// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"
下面的函数说明了hasOwnProperty()的用法:继承的属性不显示。

var triangle = {a: 1, b: 2, c: 3};

function ColoredTriangle() {
this.color = "red";
}

ColoredTriangle.prototype = triangle;

var obj = new ColoredTriangle();

for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {

console.log(`obj.${prop} = ${obj[prop]}`);

}
}

// Output:
// "obj.color = red"

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...
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()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。

Object.getOwnPropertyNames() 返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。 数组中枚举属性的顺序与通过 for...in 循环(或 Object.keys)迭代该对象属性时一致。数组中不可枚举属性的顺序未定义。
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

// 类数组对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]

// 使用Array.forEach输出属性名和属性值
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
console.log(val + " -> " + obj[val]);
});
// 输出
// 0 -> a
// 1 -> b
// 2 -> c

//不可枚举属性
var my_obj = Object.create({}, {
getFoo: {

value: function() { return this.foo; },
enumerable: false

}
});
my_obj.foo = 1;

console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]
如果你只要获取到可枚举属性,查看Object.keys或用for...in循环(还会获取到原型链上的可枚举属性,不过可以使用hasOwnProperty()方法过滤掉)。

下面的例子演示了该方法不会获取到原型链上的属性:

function ParentClass() {}
ParentClass.prototype.inheritedMethod = function() {};

function ChildClass() {
this.prop = 5;
this.method = function() {};
}

ChildClass.prototype = new ParentClass;
ChildClass.prototype.prototypeMethod = function() {};

console.log(
Object.getOwnPropertyNames(

new ChildClass()  // ["prop", "method"]

)
);
只获取不可枚举的属性节
下面的例子使用了 Array.prototype.filter() 方法,从所有的属性名数组(使用Object.getOwnPropertyNames()方法获得)中去除可枚举的属性(使用Object.keys()方法获得),剩余的属性便是不可枚举的属性了:

var target = myObject;
var enum_and_nonenum = Object.getOwnPropertyNames(target);
var enum_only = Object.keys(target);
var nonenum_only = enum_and_nonenum.filter(function(key) {

var indexInEnum = enum_only.indexOf(key);
if (indexInEnum == -1) {
    // 没有发现在enum_only健集中意味着这个健是不可枚举的,
    // 因此返回true 以便让它保持在过滤结果中
    return true;
} else {
    return false;
}

});

console.log(nonenum_only);
注:Array.filter(filt_func)方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
提示节
在 ES5 中,如果参数不是一个原始对象类型,将抛出一个 TypeError 异常。在 ES2015 中,非对象参数被强制转换为对象 。

Object.getOwnPropertyNames("foo");
// TypeError: "foo" is not an object (ES5 code)

Object.getOwnPropertyNames("foo");
// ["length", "0", "1", "2"] (ES2015 code)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103498.html

相关文章

  • JavaScript对象遍历

    摘要:简单说就是一个有一个函数,这个函数执行的返回值一定是一个对象,对象有两个属性标记迭代是否结束,标记这次迭代的结果值。 引言 遍历对象是平常工作中很常见的一个操作,几乎是日常操作,但是遍历对象真的是一件很容易的事情么,显然不是的。 常用的方式 for...in for (variable in object) {...} 这个是一个很常见的用法,相信每个人顺手都可以写出来。但是这里需要主...

    Seay 评论0 收藏0
  • JavaScriptfor从懵懂到辨明

    摘要:对于每个枚举的属性,部分都会被执行。被迭代枚举的对象。三对数组的每个元素执行一次提供的函数。没有办法终止会跳出循环,除了抛出一个异常。当到达包含值的项时,整个数组的第一个项被移除了,这导致所有剩下的项前移了一个位置。 showImg(https://segmentfault.com/img/remote/1460000015236979?w=1776&h=1080); 前言 初学Jav...

    loonggg 评论0 收藏0
  • 编写高质量JavaScript代码之数组和字典

    摘要:但实例化该构造函数仍然得到的是的实例。或者,为了避免在所有查找属性的地方都插入这段样本代码,我们可以将该模式抽象到的构造函数中。该构造函数封装了所有在单一数据类型定义中编写健壮字典的技术细节。 参考书籍:《Effective JavaScript》 数组和字典 对象是JavaScript中最万能的数据结构。取决于不同的环境,对象可以表示一个灵活的键值关联记录,一个继承了方法的面向对象数...

    izhuhaodev 评论0 收藏0
  • JavaScript for、for in、for of、forEach等使用总结

    摘要:等内置的方法和属性都是不可枚举的北京北京可以遍历可枚举但对象,包括不是它本身但存在于原型链上的属性。北京循环可迭代对象循环可迭代对象包括,,,,,类数组的对象比如对象对象以及生成器对象等。 在JavaScript中,我们经常需要去循环迭代方法操作数组对象等,常见等循环方法有 for、for in、for of、forEach等。 1.for循环 for循环是最基础常见的一种循环,圆括号...

    hlcc 评论0 收藏0
  • js遍历之for forEach in of

    摘要:语法参数当前遍历项。遍历的范围在第一次调用前就会确定。已删除的项不会被遍历到。的是由提出的,目的是作为遍历所有数据结构的统一方法。不仅可以遍历数组,还可以遍历结构,某些类似数组的对象如对象对象,对象,以及字符串。 即使是最简单的循环,其中也深藏学问 ECMAScript5(es5)有三种for循环 简单for for in forEach ECMAScript6(es6)新增 fo...

    yeyan1996 评论0 收藏0

发表评论

0条评论

MingjunYang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<