为什么在循环中使用arr.lenght(misspelt)而不是arr.length时,JavaScript为什么不警告我?我也使用严格模式

时间:2018-12-03 03:09:10

标签: javascript

我花了几个小时才发现我将单词.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]);
}

6 个答案:

答案 0 :(得分:72)

因为当您尝试获取不存在的属性时,它将返回undefined,而0 < undefinedfalse

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 < undefinedfalse。因此循环体永远不会执行。

答案 5 :(得分:1)

默认情况下,JavaScript中的所有对象都是可扩展的,这意味着您可以随时为它们添加附加属性,只需为其分配值即可。

数组没有什么不同;它们只是Array类型实例的对象(至少出于扩展目的)。

在这种情况下,您是否添加了:

Object.preventExtensions(arr);

创建数组后,然后与'use strict'结合使用,将会引发错误-如果您尝试写入到一个有错字的属性。但是对于这样的读取用法,仍然没有任何错误。您只会得到undefined

这只是您使用一种宽松类型的语言所必须经历的事情之一;如果您不小心的话,具有更大的灵活性会增加错误的风险。