我花了几个小时才发现我将单词.length
误写为.lenght
。它可以正常运行,而没有任何警告。为什么...?
我使用'use strict'
并在Node.js 10.13.0上运行。
代码:
'use strict';
let arr = [1, 2, 3, 4];
for(let i = 0; i < arr.lenght; i++) {
console.log(arr[i]);
}
答案 0 :(得分:72)
因为当您尝试获取不存在的属性时,它将返回undefined
,而0 < undefined
为false
。
let arr = [1, 2, 3, 4];
console.log(arr.lenght) // undefined
console.log(arr.qwerty) // undefined
console.log(arr.lenght < 9999) // false
console.log(arr.lenght > 9999) // false
arr.length = 7 // <-- it's not a good idea
for(let i = 0; i < arr.length; i++) {console.log(arr[i])}
编辑
我说过“ javascript不是强类型语言”,这是事实。但是,@ Voo表示,这种添加新属性的方法是基于原型的编程的功能。
我还说过.length=7
是个坏主意。阅读更多内容之后,在这种情况下,我仍然认为在添加元素之后增加length
属性有点怪异。截断,删除元素或清空数组也许很好,尽管在后一种情况下,我希望使用arr=[]
而不是arr.length=0
。
在Mozilla documentation中有一些有关length
属性的有趣示例。
JavaScript数组的length属性和数值属性是 连接的。几种内置数组方法(例如join(), slice(),indexOf()等)考虑了数组的值 被调用时的length属性。其他方法(例如push(), splice()等)也会导致更新数组的length属性。
var fruits = []; fruits.push('banana', 'apple', 'peach'); console.log(fruits.length); // 3
当属性是JavaScript数组上的属性时, 有效的数组索引,并且该索引超出当前数组的范围 数组,引擎将相应地更新数组的length属性:
fruits[5] = 'mango'; console.log(fruits[5]); // 'mango' console.log(Object.keys(fruits)); // ['0', '1', '2', '5'] console.log(fruits.length); // 6
增加长度。
fruits.length = 10; console.log(Object.keys(fruits)); // ['0', '1', '2', '5'] console.log(fruits.length); // 10
减小length属性确实会删除元素。
fruits.length = 2; console.log(Object.keys(fruits)); // ['0', '1'] console.log(fruits.length); // 2
答案 1 :(得分:14)
JavaScript数组被视为对象(尽管它们是Array的实例)。因此,当您编写arr.lenght
时,它将lenght
视为未定义对象的属性。因此,您不会出错。
它只是试图获取未定义的属性。另外,在您的情况下,由于永不满足循环条件,因此循环不会执行。
答案 2 :(得分:8)
标准JavaScript数组aren't really arrays at all¹是对象,如果您读取了不存在的对象属性(如lenght
),则会得到值undefined
(甚至在严格模式下):
console.log(({}).foo); // undefined
当您在与数字相关的undefined
或<
之类的关系运算中使用>
时,它会转换为数字,但是它得到的数字值是特殊数字{{ 1}},它具有总是导致比较为假的奇异特性:
NaN
Linter工具通常会在简单的情况下处理这些事情。
或者,TypeScript在JavaScript之上提供了一个完整的静态键入层,可以捕获这些类型的错误。
如果您愿意(这可能是过大的杀伤力),可以将Proxy包裹在对象上,当您尝试读取不存在的属性时会引发主动错误:
console.log(NaN < 0); // false
console.log(NaN > 0); // false
console.log(NaN === 0); // false
console.log(NaN === NaN); // false!!
function proactive(obj) {
return new Proxy(obj, {
get(target, propName, receiver) {
if (!Reflect.has(target, propName)) {
throw new TypeError(`Property '${propName}' not found on object`);
}
return Reflect.get(target, propName, receiver);
}
});
}
const a = proactive(["a", "b"]);
a.push("c");
for (let i = 0; i < a.length; ++i) {
console.log(a[i]);
}
console.log(`Length is: ${a.lenght}`); // Note the typo
尽管如此,运行时还是要付出很大的代价。
¹(这是我贫乏的小博客上的帖子)
答案 3 :(得分:6)
您可以轻松地向arr
对象添加新属性,JavaScript不会警告您,相反,它将尝试查找您正在调用的属性,如果未找到任何此类结果,则会是未定义的,因此实际上每次比较都是i < undefined
,因为您要调用的对象尚未创建。我建议您阅读What does "use strict" do in JavaScript, and what is the reasoning behind it?。
答案 4 :(得分:5)
循环的上限指定为lenght
,这是局部变量长度的错字。在运行时,lenght
的评估结果为undefined
,因此支票0 < undefined
为false
。因此循环体永远不会执行。
答案 5 :(得分:1)
默认情况下,JavaScript中的所有对象都是可扩展的,这意味着您可以随时为它们添加附加属性,只需为其分配值即可。
数组没有什么不同;它们只是Array
类型实例的对象(至少出于扩展目的)。
在这种情况下,您是否添加了:
Object.preventExtensions(arr);
创建数组后,然后与'use strict'
结合使用,将会引发错误-如果您尝试写入到一个有错字的属性。但是对于这样的读取用法,仍然没有任何错误。您只会得到undefined
。
这只是您使用一种宽松类型的语言所必须经历的事情之一;如果您不小心的话,具有更大的灵活性会增加错误的风险。