聊聊 Array 中的坑

原文:
https://jakearchibald.com/201…

翻译:猖獗的手艺宅

本文首发微信民众号:jingchengyideng
迎接关注,天天都给你推送新颖的前端手艺文章

Array 范例检测

function foo(obj) {
  // …
}

假定obj是一个数组,我们想要完成一些功用。比方 JSON.stringify就是一个例子,它以差别的体式格局把数组输出到其他对象。

我们能够如许做:

if (obj.constructor == Array) // …

然则关于数组的子类来讲这是毛病的:

class SpecialArray extends Array {}
const specialArray = new SpecialArray();
console.log(specialArray.constructor === Array); // false
console.log(specialArray.constructor === SpecialArray); // true

所以假如你想搜检子类的范例,那末应该用instanceof

console.log(specialArray instanceof Array); // true
console.log(specialArray instanceof SpecialArray); // true

然则当引入多个realm时,事变将会变得越发庞杂:

Multiple realms

realm包括self援用的JavaScript全局对象。 因而,能够说在worker中运转的代码与在页面中运转的代码处于差别的realm。 在iframe之间也是云云,但同源iframe也同享一个ECMAScript’代办’,这意味着对象能够穿越 realm

接着看代码:

<iframe srcdoc="<script>var arr = [];</script>"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  const arr = iframe.contentWindow.arr;
  console.log(arr.constructor === Array); // false
  console.log(arr.constructor instanceof Array); // false
</script>

这两个都是false,由于:

console.log(Array === iframe.contentWindow.Array); // false

iframe有本身的数组组织函数,它与父页面中的组织函数差别。

Array.isArray

console.log(Array.isArray(arr)); // true

Array.isArray 将为数组返回true,纵然它们是在另一个realm中建立的。 关于任何realm的Array的子类,它也会返回true。 这就是JSON.stringify内部的处置惩罚要领。

然则,这并不意味着arr有 array 要领。 有些以至一切要领都已设置为undefined,或许数组能够已将其全部原型删除:

const noProtoArray = [];
Object.setPrototypeOf(noProtoArray, null);
console.log(noProtoArray.map); // undefined
console.log(noProtoArray instanceof Array); // false
console.log(Array.isArray(noProtoArray)); // true

不管怎样,假如要根绝上述问题,能够经由过程Array原型挪用Array的要领:

if (Array.isArray(noProtoArray)) {
  const mappedArray = Array.prototype.map.call(noProtoArray, callback);
  // …
}

Symbols 与 realms

再看看这个:

<iframe srcdoc="<script>var arr = [1, 2, 3];</script>"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  const arr = iframe.contentWindow.arr;

  for (const item of arr) {
    console.log(item);
  }
</script>

上面的logs 1, 2, 3 很不有目共睹,但 for-of 轮回经由过程挪用arr[Symbol.iterator]来事情,这在某种程度上能够逾越realm。 这是如何做:

const iframe = document.querySelector('iframe');
const iframeWindow = iframe.contentWindow;
console.log(Symbol === iframeWindow.Symbol); // false
console.log(Symbol.iterator === iframeWindow.Symbol.iterator); // true

虽然每一个realm都有本身的Symbol实例,但Symbol.iterator在各个realm都是雷同的。

Symbols同时也是JavaScript中最奇特和最奇特的东西。

The most unique 多唯一性

const symbolOne = Symbol('foo');
const symbolTwo = Symbol('foo');
console.log(symbolOne === symbolTwo); // false
const obj = {};
obj[symbolOne] = 'hello';
console.log(obj[symbolTwo]); // undefined
console.log(obj[symbolOne]); // 'hello'

通报给Symbol函数的字符串只是一个形貌。 纵然在统一realm内,这些Symbol也是举世无双的。

The least unique 最小唯一性

const symbolOne = Symbol.for('foo');
const symbolTwo = Symbol.for('foo');
console.log(symbolOne === symbolTwo); // true
const obj = {};
obj[symbolOne] = 'hello';
console.log(obj[symbolTwo]); // 'hello'

Symbol.for(str) 建立一个与通报它的字符串唯一的symbol。 风趣的是它在各个realms都是一样的:

const iframe = document.querySelector('iframe');
const iframeWindow = iframe.contentWindow;
console.log(Symbol.for('foo') === iframeWindow.Symbol.for('foo')); // true

这就是Symbol.iterator大抵的事情道理。

建立本身的 ‘is’ 函数

假如我们想要建立我们本身的“is”函数并逾越realm会怎样? 好吧,Symbol许可我们如许做:

const typeSymbol = Symbol.for('whatever-type-symbol');

class Whatever {
  static isWhatever(obj) {
    return obj && Boolean(obj[typeSymbol]);
  }
  constructor() {
    this[typeSymbol] = true;
  }
}

const whatever = new Whatever();
Whatever.isWhatever(whatever); // true

纵然实例来自另一个realm,纵然它是一个子类,纵然它的原型已被删除,也是能够的。

唯一的问题是,你须要确认本身的symbol称号在一切代码中都是唯一的。 假如其他人建立了他们本身的Symbol.for('whatever-type-symbol')并使用它来示意别的东西,那末isWhatever一定返回false。

本文首发微信民众号:jingchengyideng
迎接关注,天天都给你推送新颖的前端手艺文章

    原文作者:疯狂的技术宅
    原文地址: https://segmentfault.com/a/1190000017956550
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞