JavaScript秘密花园 - Array, Array Constructor, for in loop, typeof, instanceOf
2011-12-13 16:26
721 查看
JavaScript Garden - 原文
虽然在 JavaScript 中数组是是对象,但是没有好的理由去使用 `for
in` 循环 遍历数组。 相反,有一些好的理由不去使用
由于
因此会比普通的
为了达到遍历数组的最佳性能,推荐使用经典的
上面代码有一个处理,就是通过
虽然
JavaScript 引擎在这点上做了优化,但是我们没法保证自己的代码是否运行在这些最近的引擎之上。
实际上,不使用缓存数组长度的方式比缓存版本要慢很多。
译者注: 在 Firebug 中查看此时 foo 的值是: [1, 2, 3, undefined, undefined, undefined] 但是这个结果并不准确,如果你在 Chrome 的控制台查看 foo 的结果,你会发现是这样的: [1, 2, 3] 因为在 JavaScript 中 undefined 是一个变量,注意是变量不是关键字,因此上面两个结果的意义是完全不相同的。
为
为了更好的性能,推荐使用普通的
使用
由于
来创建数组。
译者注:这里的模棱两可指的是数组的两种构造函数语法 var arr1 = new Array(arrayLength);
var arr2 = new Array(element0, element1, ..., elementN);
由于只有一个参数传递到构造函数中(译者注:指的是
需要特别注意的是,此时只有
这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免
应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。
和
注意:
由于不可能改变
注意: 由于
这个版本的代码是唯一正确的写法。由于我们使用了
如果不使用
一个广泛使用的类库 Prototype 就扩展了原生的 JavaScript 对象。
因此,但这个类库被包含在页面中时,不使用
推荐总是使用
JavaScript 中最大的设计缺陷, 因为几乎不可能从它们那里得到想要的结果。
尽管
而这个应用却不是用来检查对象的类型。
注意: 由于
那两个小括号只是用来计算一个表达式的值,这个返回值会作为
上面表格中,Type 一列表示
"object"。
Class 一列表示对象的内部属性
JavaScript 标准文档中定义:
为了获取对象的
JavaScript 标准文档只给出了一种获取
上面例子中,
译者注:
slice 截取指定位置的字符串,如下所示:
ES5 提示: 在 ECMAScript 5 中,为了方便,对
其返回值由
译者注:这种变化可以从 IE8 和 Firefox 4 中看出区别,如下所示:
上面代码会检测
这是
为了检测一个对象的类型,强烈推荐使用
因此不同的引擎实现可能不同。
除非为了检测一个变量是否已经定义,我们应尽量避免使用
操作符 一样用处不大。
有一点需要注意,
JavaScript 上下文的自定义对象。 正如 `typeof` 操作符一样,任何其它的用法都应该是避免的。
数组遍历与属性
虽然在 JavaScript 中数组是是对象,但是没有好的理由去使用 `for
in` 循环 遍历数组。 相反,有一些好的理由不去使用 for in遍历数组。
由于
for in循环会枚举原型链上的所有属性,唯一过滤这些属性的方式是使用 `hasOwnProperty` 函数,
因此会比普通的
for循环慢上好多倍。
遍历(Iteration)
为了达到遍历数组的最佳性能,推荐使用经典的 for循环。
var list = [1, 2, 3, 4, 5, ...... 100000000]; for(var i = 0, l = list.length; i < l; i++) { console.log(list[i]); }
上面代码有一个处理,就是通过
l = list.length来缓存数组的长度。
虽然
length是数组的一个属性,但是在每次循环中访问它还是有性能开销。 可能最新的
JavaScript 引擎在这点上做了优化,但是我们没法保证自己的代码是否运行在这些最近的引擎之上。
实际上,不使用缓存数组长度的方式比缓存版本要慢很多。
`length` 属性(The `length` property)
length属性的 getter 方式会简单的返回数组的长度,而 setter 方式会截断数组。
var foo = [1, 2, 3, 4, 5, 6]; foo.length = 3; foo; // [1, 2, 3] foo.length = 6; foo; // [1, 2, 3]
译者注: 在 Firebug 中查看此时 foo 的值是: [1, 2, 3, undefined, undefined, undefined] 但是这个结果并不准确,如果你在 Chrome 的控制台查看 foo 的结果,你会发现是这样的: [1, 2, 3] 因为在 JavaScript 中 undefined 是一个变量,注意是变量不是关键字,因此上面两个结果的意义是完全不相同的。
// 译者注:为了验证,我们来执行下面代码,看序号 5 是否存在于 foo 中。 5 in foo; // 不管在 Firebug 或者 Chrome 都返回 false foo[5] = undefined; 5 in foo; // 不管在 Firebug 或者 Chrome 都返回 true
为
length设置一个更小的值会截断数组,但是增大
length属性值不会对数组产生影响。
结论(In conclusion)
为了更好的性能,推荐使用普通的 for循环并缓存数组的
length属性。
使用
for in遍历数组被认为是不好的代码习惯并倾向于产生错误和导致性能问题。
Array
构造函数
由于 Array的构造函数在如何处理参数时有点模棱两可,因此总是推荐使用数组的字面语法 -
[]-
来创建数组。
[1, 2, 3]; // 结果: [1, 2, 3] new Array(1, 2, 3); // 结果: [1, 2, 3] [3]; // 结果: [3] new Array(3); // 结果: [] new Array('3') // 结果: ['3']
译者注:这里的模棱两可指的是数组的两种构造函数语法 var arr1 = new Array(arrayLength);
var arr2 = new Array(element0, element1, ..., elementN);
// 译者注:因此下面的代码将会使人很迷惑 new Array(3, 4, 5); // 结果: [3, 4, 5] new Array(3) // 结果: [],此数组长度为 3
由于只有一个参数传递到构造函数中(译者注:指的是
new Array(3);这种调用方式),并且这个参数是数字,构造函数会返回一个
length属性被设置为此参数的空数组。
需要特别注意的是,此时只有
length属性被设置,真正的数组并没有生成。 译者注:在 Firebug 中,你会看到 [undefined, undefined, undefined],这其实是不对的。在上一节有详细的分析。
var arr = new Array(3); arr[1]; // undefined 1 in arr; // false, 数组还没有生成
这种优先于设置数组长度属性的做法只在少数几种情况下有用,比如需要循环字符串,可以避免
for循环的麻烦。
new Array(count + 1).join(stringToRepeat); // 译者注:new Array(3).join('#') 将会返回 "##"
结论(In conclusion)
应该尽量避免使用数组构造函数创建新数组。推荐使用数组的字面语法。它们更加短小和简洁,因此增加了代码的可读性。
for in
循环
和 in操作符一样,
for in循环同样在查找对象属性时遍历原型链上的所有属性。
注意:
for in循环不会遍历那些
enumerable设置为
false的属性;比如数组的
length属性。
// 修改 Object.prototype Object.prototype.bar = 1; var foo = {moo: 2}; for(var i in foo) { console.log(i); // 输出两个属性:bar 和 moo }
由于不可能改变
for in自身的行为,因此有必要过滤出那些不希望出现在循环体中的属性, 这可以通过
Object.prototype原型上的 `hasOwnProperty` 函数来完成。
注意: 由于
for in总是要遍历整个原型链,因此如果一个对象的继承层次太深的话会影响性能。
使用 `hasOwnProperty` 过滤(Using `hasOwnProperty` for filtering)
// foo 变量是上例中的 for(var i in foo) { if (foo.hasOwnProperty(i)) { console.log(i); } }
这个版本的代码是唯一正确的写法。由于我们使用了
hasOwnProperty,所以这次只输出
moo。
如果不使用
hasOwnProperty,则这段代码在原生对象原型(比如
Object.prototype)被扩展时可能会出错。
一个广泛使用的类库 Prototype 就扩展了原生的 JavaScript 对象。
因此,但这个类库被包含在页面中时,不使用
hasOwnProperty过滤的
for in循环难免会出问题。
最佳实践(Best practices)
推荐总是使用 hasOwnProperty。不要对代码运行的环境做任何假设,不要假设原生对象是否已经被扩展了。
typeof
操作符
typeof操作符(和 `instanceof` 一起)或许是
JavaScript 中最大的设计缺陷, 因为几乎不可能从它们那里得到想要的结果。
尽管
instanceof还有一些极少数的应用场景,
typeof只有一个实际的应用(译者注:这个实际应用是用来检测一个对象是否已经定义或者是否已经赋值),
而这个应用却不是用来检查对象的类型。
注意: 由于
typeof也可以像函数的语法被调用,比如
typeof(obj),但这并是一个函数调用。
那两个小括号只是用来计算一个表达式的值,这个返回值会作为
typeof操作符的一个操作数。 实际上不存在名为
typeof的函数。
JavaScript 类型表格(The JavaScript type table)
Value Class Type ------------------------------------- "foo" String string new String("foo") String object 1.2 Number number new Number(1.2) Number object true Boolean boolean new Boolean(true) Boolean object new Date() Date object new Error() Error object [1,2,3] Array object new Array(1, 2, 3) Array object new Function("") Function function /abc/g RegExp object (function in Nitro/V8) new RegExp("meow") RegExp object (function in Nitro/V8) {} Object object new Object() Object object
上面表格中,Type 一列表示
typeof操作符的运算结果。可以看到,这个值在大多数情况下都返回
"object"。
Class 一列表示对象的内部属性
[[Class]]的值。
JavaScript 标准文档中定义:
[[Class]]的值只可能是下面字符串中的一个:
Arguments,
Array,
Boolean,
Date,
Error,
Function,
JSON,
Math,
Number,
Object,
RegExp,
String.
为了获取对象的
[[Class]],我们需要使用定义在
Object.prototype上的方法
toString。
对象的类定义(The Class of an object)
JavaScript 标准文档只给出了一种获取 [[Class]]值的方法,那就是使用
Object.prototype.toString。
function is(type, obj) { var clas = Object.prototype.toString.call(obj).slice(8, -1); return obj !== undefined && obj !== null && clas === type; } is('String', 'test'); // true is('String', new String('test')); // true
上面例子中,
Object.prototype.toString方法被调用,this 被设置为了需要获取
[[Class]]值的对象。
译者注:
Object.prototype.toString返回一种标准格式字符串,所以上例可以通过
slice 截取指定位置的字符串,如下所示:
Object.prototype.toString.call([]) // "[object Array]" Object.prototype.toString.call({}) // "[object Object]" Object.prototype.toString.call(2) // "[object Number]"
ES5 提示: 在 ECMAScript 5 中,为了方便,对
null和
undefined调用
Object.prototype.toString方法,
其返回值由
Object变成了
Null和
Undefined。
译者注:这种变化可以从 IE8 和 Firefox 4 中看出区别,如下所示:
// IE8 Object.prototype.toString.call(null) // "[object Object]" Object.prototype.toString.call(undefined) // "[object Object]" // Firefox 4 Object.prototype.toString.call(null) // "[object Null]" Object.prototype.toString.call(undefined) // "[object Undefined]"
测试为定义变量(Testing for undefined variables)
typeof foo !== 'undefined'
上面代码会检测
foo是否已经定义;如果没有定义而直接使用会导致
ReferenceError的异常。
这是
typeof唯一有用的地方。
结论(In conclusion)
为了检测一个对象的类型,强烈推荐使用 Object.prototype.toString方法; 因为这是唯一一个可依赖的方式。正如上面表格所示,
typeof的一些返回值在标准文档中并未定义,
因此不同的引擎实现可能不同。
除非为了检测一个变量是否已经定义,我们应尽量避免使用
typeof操作符。
instanceof
操作符
instanceof操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义。 如果用来比较内置类型,将会和 typeof
操作符 一样用处不大。
比较自定义对象(Comparing custom objects)
function Foo() {} function Bar() {} Bar.prototype = new Foo(); new Bar() instanceof Bar; // true new Bar() instanceof Foo; // true // 如果仅仅设置 Bar.prototype 为函数 Foo 本省,而不是 Foo 构造函数的一个实例 Bar.prototype = Foo; new Bar() instanceof Foo; // false
`instanceof` 比较内置类型(Using `instanceof` with native types)
new String('foo') instanceof String; // true new String('foo') instanceof Object; // true 'foo' instanceof String; // false 'foo' instanceof Object; // false
有一点需要注意,
instanceof用来比较属于不同 JavaScript 上下文的对象(比如,浏览器中不同的文档结构)时将会出错, 因为它们的构造函数不会是同一个对象。
结论(In conclusion)
instanceof操作符应该仅仅用来比较来自同一个
JavaScript 上下文的自定义对象。 正如 `typeof` 操作符一样,任何其它的用法都应该是避免的。
相关文章推荐
- [翻译]JavaScript秘密花园 - Array, Array Constructor, for in loop, typeof, instanceOf
- JavaScript秘密花园 - Array, Array Constructor, for in loop, typeof, instanceOf
- JavaScript秘密花园 - Array, Array Constructor, for in loop, typeof, instanceOf
- [翻译]JavaScript秘密花园 - Array, Array Constructor, for in loop, typeof, instanceOf
- Array, Array Constructor, for in loop, typeof, instanceOf
- Best Way to Loop Through an Array in JavaScript
- javascript特殊运算符(in,instanceof,typeof,delete,void,逗号)
- A for loop javascript event dynamically binding with customized data as paramete passed in test
- JavaScript中typeof,instanceof,hasOwnProperty,in用法区别
- A for loop javascript event dynamically binding with customized data as paramete passed in test
- 【javascript】for(variable in array)
- JavaScript中typeof,instanceof,hasOwnProperty,in的用法和区别
- PhoneGap: open source development framework for building cross-platform mobile apps, Build apps in HTML and JavaScript
- JavaScript中的delete,typeof,instanceof运算符
- javascript中的in_array()在数组中查找元素值
- JavaScript中typeof和instanceof的介绍
- javascript for/in 循环遍历对象属性
- JavaScript 中的for/in