Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重学js —— Array.isArray——严格判定JavaScript对象是否为数组 #55

Closed
lizhongzhen11 opened this issue Nov 14, 2019 · 0 comments
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN

Comments

@lizhongzhen11
Copy link
Owner

lizhongzhen11 commented Nov 14, 2019

严格判定JavaScript对象是否为数组

先看MDN:Array.isArray()

能准确区分数组和类数组对象!

接下来是翻译:

JavaScript中的类型校验问题

JavaScript的 typeof 操作符有着令人困惑的表现行为:typeof null === "object",而 typeof null !== "null"。这个错误会让人震惊,但我们基本上已经习惯了。可能更重要的是,有一个故障安全的解决方法:简单直接的用 === (全等)比较,例如 v === null 来进行判断。

确定值是否为数组

typeof null === "object" 可能是最常见的类型校验错误,不过还存在其它的类型校验错误。一个不太普遍但同样令人困惑的问题是 如何确定对象是否为数组。你可能会有个简单的解决方案:

if (o instanceof Array) {
  // 太糟糕了
}

在某些情况下,上面代码运行完美。但是如果存在 多个全局变量 就会出现问题。

ECMAScript规范描述了当执行一串代码时调用的环境和机制。语言构造的语法和基本语义当然很重要,但是如果没有ECMAScript中的内置方法和对象编码,就不能很好工作。这些方法和对象被全局对象使用,这里就是奇怪事情的发生地。ECMAScript 3 环境隐式假定存在单个全局变量(或者,也许是每个独立部分都有自己的环境,彼此之间没有相互作用)并且没有解决多个全局变量的想法。

但是,多个全局变量是浏览器的基础;每个 window 对象是该页面包含或引用的脚本的全局对象。那么在不同 window 中的数组呢?

当两个页面都增加 Array.prototype 时,将两个 window 的数组作为同一 Array 构造函数的实例,共享相同 Array.prototype 的 变化风险 非常大(更不用说当一个页面为恶意页面时的安全问题!),因此每个窗口中的 ArrayArray.prototype 必须不同。所以,仅当 o 是由该页面原始 Array 构造器创建的数组(或者使用该页面数组字面量创建),o instanceof Array 才会运行正确。

是否还有其他可以确定值是数组的方法?

  • o.constructor === Array 是一种方法,但和 instanceof 有相同的问题。
  • 另一个选择依赖于 “鸭子类型”思想,当值看起来像数组,那么它就是数组。
  • 沿着 constructor 检查,您可以检查其他数组方法(例如 pushconcat),或者检查 length 属性,但是非数组对象也可以有这些同名属性。
  • 使用Object.prototype.toString.call(o) === "[object Array]",但是依赖于 Object.prototype.toStringFunction.prototype.call 未被改变。

来到 Array.isArray

由于这些原因,ES5定义了 Array.isArray 方法来完全解决该问题。

Array.isArray(Array.prototype); // true

function test(fun, expect) { 
  if (fun() !== expect) alert("FAIL: " + fun); 
}

test(function() { 
  return Array.isArray([]); 
}, true);

test(function() { 
  return Array.isArray(new Array); 
}, true);

test(function() { 
  return Array.isArray(); 
}, false);

test(function() { 
  return Array.isArray({ constructor: Array }); 
}, false);

test(function() { 
  return Array.isArray(   
    { 
      push: Array.prototype.push, 
      concat: Array.prototype.concat 
    }
  ); 
}, false);

test(function() { 
  return Array.isArray(17); 
}, false);

Object.prototype.toString = function() { return "[object Array]"; };

test(function() { 
  return Array.isArray({}); 
}, false);

test(function() { 
  return Array.isArray({ __proto__: Array.prototype }); 
}, false);

test(function() { 
  return Array.isArray({ length: 0 }); 
}, false);

var w = window.open("about:blank");
w.onload = function()
{
  test(function() { 
    return Array.isArray(arguments); 
  }, false);
  test(function() { 
    return Array.isArray(new w.Array); 
  }, true);
};

规范

  1. Array.isArray( arg )
  2. IsArray(arg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN
Projects
None yet
Development

No branches or pull requests

1 participant