最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 【JS学习笔记】Symbol

    正文概述 掘金(一道风痕)   2021-06-27   552

    Symbol

    符号是一个原始数据类型、和Number、String、Boolean不同,它没有字面量语法。

    普通符号创建

    const syb = Symbol();
    const syb1 = Symbol("对符号的描述");
    

    没有字面量语法的创建方式,只能通过Symbol("描述")方法创建。

    不能把Symbol当作构造函数调用,new Symbol()会报错。

    可以传入一个String参数,用于描述这个符号实例。不是String类型会转换成String。

    特点

    1. 没有字面量。

    2. typeof Symbol()返回"symbol"。

    3. 符号实例是唯一的、不可变的。

      const syb = Symbol();
      const syb1 = Symbol();
      syb !== syb1
      
    4. 符号可以作为对象的属性名存在,称为符号属性。

      • 开发者可以通过精心的设计,让这些属性无法通过常规方式被外界访问(私有属性)。利用闭包,将符号实例作为对象的属性名,无法访问符号实例,自然无法通过对象的属性名访问属性了。说明:[]为计算属性名,里面可以放表达式。

        var person = (function() {
            const name = Symbol("私有属性_名字");
            return {
                [name]: "Herb",
                getName() {
                    return this[name];
                }
            }
        }())
        
      • 符号属性是不能枚举的,因此在 for-in 循环、Object.keys 方法都无法读取到符号属性。

      • Object.getOwnPropertyNames尽管可以得到自身所有无法枚举的属性名,但是仍然无法读取到符号属性名。

      • Object.getOwnPropertyDescriptor获取某个对象的某个属性和符号的属性描述符对象(该属性必须直接属于该对象)。

      • ES6新增Object.getOwnPropertySymbols方法,可以读取符号属性名,返回一个数组。

    5. 符号无法被隐式转换,因此不能被用于数学运算、字符串拼接或其他隐式转换的场景,但符号可以显式的转换为字符串,通过 String 转型函数进行转换即可(实际上是调用toString()方法)。

      var syb = Symbol("符号");
      var sybStr = String(syb);
      String(syb) == syb.toString()
      
    6. 如果想使用符号包装对象,可以借用Object()函数

      var sybObj = Object(Symbol("符号对象"));
      typeof sybObj == "object"
      sybObj.constructor
      // ƒ Symbol() { [native code] }
      sybObj instanceof Symbol
      // true
      

    共享符号

    1. for

      通过Symbol.for("符号描述")创建共享符号,可以在不同对象中用相同的符号作为属性名。

      创建后,会把符号存储在全局符号注册表中,以符号描述作为键,符号实例作为名。

      调用Symbol.for("查找")时,会先查看注册表中有没有这个符号描述("查找"),如果没有就创建一个Symbol("查找"),如果有的话,就根据键返回名。

      let foo = Symbol.for("foo");
      let foo1 = Symbol.for("foo");
      foo == foo1
      
      let bar = Symbol("bar");
      let bar1 = Symbol.for("bar");
      bar !== bar1
      
      let unDes = Symbol.for();
      // Symbol(undefined)
      

      内部实现大概类似如此:

      const symbolFor = (() => {
          const global = {};
          return des => {
              if(!global[des]) {
                  global[des] = Symbol(des);
              }
              return global[des];
          }
      })();
      

      利用立即执行函数保存global变量,形成全局符号注册表。

      作为参数传给Symbol.for()的任何值(除了符号实例)都会被转换为字符串。如果是符号实例就会抛出TypeError,应该是经过了特殊处理。

    2. keyFor

      Symbol.keyFor()来查询全局注册表,接受一个符号实例,返回全局符号注册表中对应的键(符号描述),如果不是全局符号,就返回undefined。

      如果参数不是符号,则抛出TypeError。

    知名符号

    ES6引入了一批常用内置符号,用于暴露语言内部行为。

    1. Symbol.hasInstance

      这个符号作为一个属性表示一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例。

      这个属性定义在Function原型上,所有函数和类上都可以调用。

      在ES6中,instanceof操作符会使用Symbol.hasInstance函数来确定关系。

      class Person{
          constructor(name, age, sex) {
              this.name = name;
              this.age = age;
              this.sex = sex;
          }
      }
      

      var p = new Person("Herb", 18, "男");
      var isPersonInstance = p instanceof Person;
      // true
      

      p在Person的原型链上,p的隐式原型__proto__就是Person的原型。p.__proto__ == Person.prototype结果为true。

      isPersonInstance = Person[Symbol.hasInstance](p);
      // true
      

      两者方式是等价的。


      Person instanceof Function
      // true
      Person[Symbol.hasInstance] === Function.prototype[Symbol.hasInstance] 
      // true
      

      Symbol.hasInstance方法定义在Function的原型上,因为Person构造函数是由Function创建的,所以Person继承了Function。

      在Person及其原型链上寻找Symbol.hasInstance方法,由于没有在Person上重写,最后找到了Function.prototype。

      let propertyObj = Object.getOwnPropertyDescriptor(Function.prototype, Symbol.hasInstance);
      // writable: false
      

      查看其属性描述符发现,Symbol.hasInstance不能被改写。

      所以不能直接用:

      Person[Symbol.hasInstance] = function() {
          console.log("重写Function.prototype的方法");
          return false;
      }
      

      会在原型链上查找有无Symbol.hasInstance属性,最后发现Function原型上有,修改后发现修改失败。

      所以可以直接在Person类中写一个静态方法。

      var obj = {};
      Object.prototype.name = "herb";
      Object.defineProperty(Object.prototype, "name", { writable: false });
      obj.name = "ice";
      obj.name   // "herb"
      obj        // {}
      

      属性是无法修改的。也不会出现在obj上。

      var obj = {}
      Object.prototype.name = "herb";
      obj.name = "ice";
      obj.name          // "ice"
      obj               // { name: "ice" }
      

      没有设置属性是不可写的时候,是能添加到对象上的。


      class Person{
          constructor(name, age, sex) {
              this.name = name;
              this.age = age;
              this.sex = sex;
          }
          // 在Person类的prototype上定义了Symbol.hasInstance方法,无意义
          [Symbol.hasInstance]() {
              console.log("Person改写Function原型上的方法");
              return false;
          }
          // 在类上重写了函数,是类的静态方法
          static [Symbol.hasInstance]() {
              console.log("Person改写Function原型上的方法");
              return false;
          }
      }
      

      p instanceof Person
      Person[Symbol.hasInstance](p)
      // Person改写Function原型上的方法
      // false
      

      使用类的静态方法书写形式,可以重写。

      Object.defineProperty(Person, Symbol.hasInstance, {
          value: function() {
              console.log("defineProperty重写")
              return false;
          }
      })
      // defineProperty重写
      // false
      

      也可以定义一个对象的属性,也就是类的属性(静态)。

      知名符号可以让我们更多的参与到js语言的内部实现上来。

    2. Symbol.toPrimitive

      这个符号作为属性表示一个方法,该方法将对象转换为相应的原始值。

      通过在一个实例的Symbol.toPrimitive属性上定义一个函数,这个函数可以改变默认行为。

      class Team{
          constructor(teamName, teamNum) {
              this.teamName = teamName;
              this.teamNum = teamNum;
              this[Symbol.toPrimitive] = function(type){
                  switch(type) {    
                      case "number":
                          return this.teamNum;
                      case "default":
                      case "string":
                          return this.teamName;
                  }
              }
          } 
      }
      

      const team = new Team("特种部队", 4);
      Number(team)     // 4
      +team            // 4
      team + 1         // "特种部队1"
      team + "nb"      // "特种部队nb"
      team * 5		 // 20
      String(team)	 // "特种部队"
      

      进行数学运算时,type为number。

      出现加号和字符串时,type为default。

      调用String()转型函数时,type为string。

      这里还要再研究一下。

      把这个属性放在实例中,就会重复创建多次。也可以放在原型链上。


      class Team{
          constructor(teamName, teamNum) {
              this.teamName = teamName;
              this.teamNum = teamNum;
          }
          [Symbol.toPrimitive](type) {
              switch(type) {    
                  case "number":
                      return this.teamNum;
                  case "default":
                  case "string":
                      return this.teamName;
              }
          }
      }
      

      不过这样会对Team.prototype产生影响。

      String(Team.prototype)
      // "undefined"
      
    3. Symbol.toStringTag

      这个符号作为一个属性表示一个字符串,用于创建对象的默认字符串描述。

      由内置方法Object.prototype.toString()使用。

      通过toString()方法获取对象标识时,会检索由Symbol.toStringTag指定的实例标识符,默认为"Object"。

      class Bar {
          constructor() {
              this[Symbol.toStringTag] = "Bar";
          }
          // [Symbol.toStringTag] = "Bar"
      }
      var bar = new Bar();
      bar.toString();
      // "[object Bar]"
      

      写成实例属性或类的静态属性都可以。


    起源地下载网 » 【JS学习笔记】Symbol

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元