for…infor…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// 传入 undefinedObject.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)