符号:改变内置行为的众所周知的符号

时间:2018-01-19 18:11:49

标签: javascript ecmascript-6

据我所知,JavaScript ES6中的Symbol原语对两件事特别有用:

  1. Object属性
  2. 创建唯一键
  3. 覆盖标准的内置JavaScript Object方法,属性和运算符
    • 例如:在Symbol.hasInstance运行
    • 之前调用instanceof
    • 因此,如果我们创建Symbol.hasInstance的自定义版本,我们可以覆盖instanceof的行为
  4. 我的基本问题是:为什么使用Symbol来覆盖这些功能?我们不能直接覆盖它们吗?

    例如:覆盖String.prototype.match()而不是Symbol.match

    编辑:同意直接覆盖instanceof不起作用的评论者,因此使用match()作为示例。

1 个答案:

答案 0 :(得分:3)

在你的问题中你没有详细说明,但从一些推论和评论来看,你似乎误解了众所周知的符号如何与现有类型进行交互。这导致您误解了他们如何改进可能的ES5全球覆盖解决方案。

了解String.match的值是符号非常重要,而不是匹配功能。这几乎就像某人已经完成了

Symbol.match = Symbol("match");

在程序的顶部创建一个新的Symbol,并将其设置为全局属性,以便任何人都可以从任何地方访问它。

这与String.prototype.match的值形成对比,"foo".match(...)是开发人员调用String.prototype.match时使用的实际函数。

您似乎想象String.prototype.match = function(obj) { return Symbol.match(obj); }; 喜欢

String.prototype.match = function(obj) {
  // Any object that has a `Symbol.match` property 
  // will just call that property. This includes every
  // existing RegExp object.
  if (obj != null && obj[Symbol.match] !== undefined) {
    return obj[Symbol.match](this);
  }

  // Otherwise create a new regex to match against
  // match match using that. This is to handle strings, e.g
  // "foo".match("f") 
  const reg = new RegExp(obj);
  return reg[Symbol.match](this);
};

情况并非如此。查看实际实现的简化示例可以帮助您理解:

obj[Symbol.match](this);

并记住,Symbol.match()未调用obj,它正在使用名称 Symbol.matchvar regexp = new RegExp("little"); var result = "a little pattern".match(regexp); 读取一个属性,然后调用由此产生的功能。

  

为什么使用Symbol来覆盖这些功能?

希望这个例子让这个理由更加清晰。

var regexp = new RegExp("little");
var result = regexp[Symbol.match]("a little pattern");

基本上与做

相同
class MyOwnMatcher {
  constructor(word) {
    this.word = word;
  }

  [Symbol.match](haystack) {
    return haystack.indexOf(this.word);
  }
}

var index = "a little pattern".match(new MyOwnMatcher("little"));
// index === 2

那么为什么这很重要?因为现在当您设计API来处理文本时,您并不局限于使用正则表达式。我可以把我自己的整个图书馆作为

var original = String.prototype.match;
String.prototype.match = function(arg) {
  if (arg instanceof MyOwnMatcher) return this.indexOf(arg);

  return original.apply(this, arguments);
};

最重要的是,我无需更改任何全局变量即可完成此操作。在JS代码中,通常认为修改全局变量是不好的做法,除非您正在填充官方推荐和采用的API。您可以实现上述内容,如

int

但它非常丑陋,容易出错,并且修改了一个不是您自己代码中定义的全局对象。

您基本上可以将众所周知的符号视为实现由单独代码定义的接口的一种方式。