Javascript 符号:用于更改内置行为的众所周知的符号

Javascript 符号:用于更改内置行为的众所周知的符号,javascript,ecmascript-6,Javascript,Ecmascript 6,据我所知,JavaScript ES6中的符号原语在以下两方面特别有用: 为对象属性创建唯一键 重写标准内置JavaScript对象方法、属性和运算符 例如:Symbol.hasInstance在(之前)instanceof运行时调用 因此,如果我们创建一个自定义版本的Symbol.hasInstance,我们就可以覆盖instanceof 我的基本问题是:为什么要使用符号来覆盖这些函数?我们不能直接覆盖它们吗 例如:覆盖String.prototype.match()而不是Symbol.m

据我所知,JavaScript ES6中的
符号
原语在以下两方面特别有用:

  • 对象
    属性创建唯一键
  • 重写标准内置JavaScript
    对象
    方法、属性和运算符
    • 例如:
      Symbol.hasInstance
      在(之前)
      instanceof
      运行时调用
    • 因此,如果我们创建一个自定义版本的
      Symbol.hasInstance
      ,我们就可以覆盖
      instanceof
  • 我的基本问题是:为什么要使用
    符号
    来覆盖这些函数?我们不能直接覆盖它们吗

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


    编辑:同意评论者的观点,即直接重写instanceof不起作用,因此使用match()作为示例。

    您在问题中的阐述不够详细,但从一些推论和评论来看,您似乎误解了知名符号是如何与现有类型交互的。这导致您误解了它们是如何改进可能的ES5全局覆盖解决方案的

    重要的是要理解
    String.match
    的值是一个符号,而不是用于匹配的函数。这几乎就像是有人做了一样

    Symbol.match = Symbol("match");
    
    在程序顶部创建新符号,并将其设置为全局属性,以便任何人都可以从任何位置访问它

    这与
    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](这个)
    没有调用
    Symbol.match()
    ,它正在从
    obj
    读取名为
    Symbol.match
    的属性,然后调用结果函数

    为什么要使用符号覆盖这些功能

    希望这个例子能让这背后的道理更清楚

    var regexp = new RegExp("little");
    var result = "a little pattern".match(regexp);
    
    从本质上讲,这与

    var regexp = new RegExp("little");
    var result = regexp[Symbol.match]("a little pattern");
    
    那么,这有什么关系呢?因为现在,当您设计一个API来处理文本时,您不仅限于使用正则表达式。我可以把我自己的整个图书馆当作

    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
    
    最重要的是,我能够做到这一点,而无需更改任何全局变量。在JS代码中,通常认为修改globals是不好的做法,除非您正在填充官方指定和采用的API。您可以像这样实现上述功能

    var original = String.prototype.match;
    String.prototype.match = function(arg) {
      if (arg instanceof MyOwnMatcher) return this.indexOf(arg);
    
      return original.apply(this, arguments);
    };
    
    但它非常丑陋,容易出错,并且修改了一个全局对象,而这个对象不是您在自己的代码中定义的对象


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

    如何直接重写instanceof?同意,instanceof不能直接重写。因此,我使用match()作为示例。fwiw无法在
    元素
    .files
    属性处实现aribtrary
    File
    对象的设置,而不使用
    符号。迭代器
    FileList
    对象的属性,即使那个特定的实现并没有完全满足需求,为什么要覆盖现有的原型方法呢?如果目标是提供通用重写机制,则要求每个库重写全局函数将极易出错。为特定全局定义函数提供不同功能的另一种方法是使用
    ,这将不会重写具有相同名称的全局本机函数。具体的方法取决于你想要达到的目标,这是非常有帮助的,谢谢。因此,总而言之,众所周知的符号允许您为一个特定对象创建自定义函数,全局内置对象将使用该函数(代替其自身的方法)。关键点是:只有使用符号的一个对象的行为才会发生变化。say.match()的所有其他用法将不受影响。这是对其目的的正确理解吗?(附言:很抱歉回复晚了,我一直在旅行)