JavaScript之数据类型

253 阅读15分钟

数据类型

  • 简单数据类型(原始类型) Undefined、Null、Boolean、Number、String、Symbol(符号)
  • 复杂数据类型(对象) Object(无序名值对的集合)

Undefined

  • 使用 var let const 声明了变量但是没有初始化赋值时,就相当于给变量赋值了undefined
let message;
console.log(message == undefined) // true
  • 正常情况下不用显著的给变量设置undefined,undefined只是用来初始化变量,在ECMA-262第三版之前是不存在的,增加undefined与null本质的区别,null是明确确定空对象指针
undefined == null  // true 会转换操作数
  • undefined是假值

Null

  • 定义变量是对象,但是当时又没有哪个对象可保存,可以用null来填充,既能保持null是空对象指针的语义,又能与undefined区分开,又能检查变量是否被重新赋予了对象的引用
  • null 是假值

Boolean

  • 字面量:true、false
  • 区分大小写
  • 转型函数 Boolean()
    • Boolean 类型
      • 转换为true Boolean(true)
      • 转换为false Boolean(false)
    • String
      • 转换为true Boolean('dd') // 非空字符串
      • 转换为false Boolean('') // 空字符串
    • Number
      • 转换为true Boolean(1) // 非0数值 无穷大也是 true Infinity Boolean(Infinity) // true Boolean(-Infinity) // true
      • 转换为false Boolean(0) 0 和NaN
    • Object
      • 转换为true Boolean({}) 任意对象 Boolean(function() {})
      • 转换为false Boolean(null) 空指针
    • Undefined
      • 转换为true 没有
      • 转换为false Boolean(undefined)
  • 流程控制语句会自动执行其他类型值转换Boolean值的转换

Number

  • 0 === -0 ﹢0永远 === -0
  • 整数和浮点数
    • 浮点数 科学计数法
      • let floatNum = 3.125e7; // 等于31250000
      • 规则:数值后面跟一个小写的e字母,加上乘以10的多少次幂
  • 值的范围
    • infinity 无穷大, -infinity负无穷大,不能在进行任何计算
    • isFinite() 判断是不是最大值函数
    • Number.MAX_VALUE 保存最大的值 1.797 693 134 862 315 7e+308
    • Number.MIN_VALUE 保存最小的值 5e-324
    • Number.NEGATIVE_INFINITY 捕获 -Infinity
    • Number.POSITIVE_INFINITY 获取 Infinity
  • NaN
    • 表示不是数值,返回数值操作失败,不会抛出错误
    • 不合法的转换数值操作都会返回NaN, 0/0
    • 分母是0时,返回 Infinity 分母是-0,返回-Infinity
    • 任何涉及与NaN操作都会返回NaN
    • NaN 不等于 包含NaN在内的任何值 NaN == NaN // false
    • isNaN() 参数是任何数据类型,判断参数是否不是数值
      • 能直接转数值的 返回 false
      • 不能直接转数值的 返回 true
      • 转换过程 先调用对象的valueOf(),能转换,直接返回,不能,在调用toString(),并测试转换值
  • 数值转换
    • 转换函数 Number()
      • 转型函数,参数是任意类型
      • Boolean 类型,true为1 false为0
      • 数值,直接返回
      • Null 转为0
      • Undefined 转为 NaN
      • String
        1. 字符串类型数值,包含'-','+',直接转为10进制,会自动忽略0,Number('01) // 1, Number('-00001') // -1
        2. 有效的字符串浮点类型数值,会直接转换相应的浮点数,会忽略0
        3. 包含有效的十六进制格式。'0xf',转换该十六进制对应的十进制的整数值
        4. 空字符串,转0
        5. 其余字符串会转为 NaN
        let num1 = Number("Hello world!");  // NaN 
        let num2 = Number(""); // 0 
        let num3 = Number("000011"); // 11 
        let num4 = Number(true);// 1
        
      • 对象类型 转为NaN 规则 调用valueOf(),按string类型转换返回值,若是NaN,在调用toString(),按照转换字符串的规则进行转换
    • parseInt()
      • parseInt(value, 转换进制) // 2、8、10、16 避免系统出错,应该传第二个参数
      • 专注字符串转数值
      • 最前面的空格会被忽略,从第一个非空字符串开始转换
      • 第一个字符不是数值字符,加号+,减号-,返回NaN // parseInt(' i1ddd1') // Nan
      • 第一个字符是数值字符串,或者以加号+或者减号-,则会继续检测每个字段,直到结束,如果遇到非数值字符换转是数值的那些值 // parseInt(' 1dfdfd+0ddd1') // 1
      • 遇到小数点也会忽略 parseInt(' 1.34') // 1
      • 例子
        1. let num1 = parseInt("1234blue") // 1234
        2. let num2 = parseInt("") // NaN
        3. let num3 = parseInt("0xA") // 10,解释为十六进制整数
        4. let num4 = parseInt(22.5) // 22
        5. let num5 = parseInt("70") // 70,解释为十进制值
        6. let num6 = parseInt("0xf")// 15,解释为十六进制整数
        7. let num1 = parseInt("10", 2) // 2,按二进制解析
        8. let num2 = parseInt("10", 8) // 8,按八进制解析
        9. let num3 = parseInt("10", 10) // 10,按十进制解析
        10. let num4 = parseInt("10", 16) // 16,按十六进制解析
    • parseFloat()
      • 与parseInt() 转换基础相同同,主要有几点不同
      • 只能转换10进制
      • 第一次出现的小数点是有效的,后面无效
        let num1 = parseFloat("1234blue") // 1234 按整数解析
        let num2 = parseFloat("0xA") // 0
        let num3 = parseFloat("22.5") // 22.5
        let num4 = parseFloat("22.34.5") // 22.34
        let num5 = parseFloat("0908.5") // 908.5
        let num6 = parseFloat("3.125e7") // 31250000
        

String

  • 声明 单引号'', 双引号"", 反引号``, 以什么引号开头就要以什么引号结束
    1. let firstName = "John"
    2. let firstName = 'John'
    3. let firstName = John
  • 字符字面量
    • 用来表示非打印字符或其他用途字符,可以在字符串的任意位置
    • 常见字面量
      1. \n 换行
      2. \t 制表
      3. \b 退格
      4. \r 回车
      5. \f 换页
      6. \ 反斜杠(\)
      7. ' 单引号(')
      8. " 双引号(")
      9. ` 反引号(`)
      10. \xnn 以十六进制编码nn表示的字符(n表示十六进制数字0~F),'\x42' 表示 'A'
      11. \unnnn 以十六进制编码nnnn表示Unicode字符(n表示十六进制数字0~F)'\u03a3' 表示希腊字符 'Σ'
    • length 属性
      • 含有双字节字符,length不准确
  • 字符串特点
    • 声明的字符串是不可变的,修改变量值过程是先销毁原始字符串,在将新的字符串赋值该变量
    • 早期浏览器拼接字符串非常慢,现已针对性优化
  • 转为字符串
    • toString()
      • 返回当前值的字符串等价物
        let age = 11;
        let ageAsString = age.toString() // 字符串"11"
        let found = true
        let foundAsString = found.toString() // 字符串"true"
      
      • 支持数值、布尔值、对象、字符值
        1. 字符串调用toString(),返回自己的一个副本
      • null、undefined没有toString()
      • 接受底数参数 默认是返回十进制的字符串
        let value1 = 10; let value2 = true; let value3 = null; let value4;
        console.log(String(value1));  // "10"
        console.log(String(value2));  // "true"
        console.log(String(value3));  // "null"
        console.log(String(value4));  // "undefined"
        
    • 用加号操作符加上空字符串
      • '' + 1 // '1'
  • 模板字面量
    • `` 反引号,可以保留换行符,可以跨行定义字符
    • 定义模板方便
  • 字符串插值
    • 可以在一个连续定义中插入一个或者多个值
    • 是一种特殊的语法表达式,求值后得到的是字符串
    • 写法 ${} 插入的值都会使用toString()强制转型为字符串
    • 插值内可以调用函数和方法
  • 模板字面量标签函数
    • 通过标签函数可以自定义插值行为
    • 标签函数接受被插值记号分割后的模板和表达式求值的结果
    • 标签函数本身是一个常规函数,通过前缀到模板字面量来应用自定义行为
    • 标签函数接受参数为原始字符串数组和每个表达式的求值结果
      • 原始字符串数组 第一个参数总是接收这个插值左右两边的字符,并存入数组
      • 每个表达式的求值结果 有多少个插值就有多少个参数可以用剩余参数
        let a = 6; let b = 9;
        function simpleTag(strings, aValExpression, bValExpression, sumExpression) {
          console.log(strings);   // ["", " + ", " = ", ""]
          console.log(aValExpression);   // 6
          console.log(bValExpression);   // 9
          console.log(sumExpression);   // 15
          return 'foobar';
        }
        let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
        
    • 标签函数返回值是自定义模板字面量后字符串
    • 例子
      // 把目标字符串中的数值格式化为美元
      function format(strings,...values){
        return strings.reduce((s,v,idx)=>{
            if(idx>0){
                const prev=values[idx-1]
                if(typeof prev==='number'){
                    s+=`$${prev.toFixed(2)}`
                }else{
                    s+=prev
                }
            }
            return s+v
        },'')
      }
      const item='orange'
      const price=3.5554
      const text=format`the ${item}'s price is ${price}`
      console.log(text) // the orange's price is $3.56
      

Symbol

  • es6 新增基本数据类型,符合是原始值,符合实例是唯一、不可变的。符合用途确保对象属性使用唯一标识符,且不会发生属性冲突
  • symbol基本使用
    • 使用Symbol()函数初始化,typeof会返回symbol类型
    • 调用时可以传入一个字符串参数作为描述,以方便将来通过这个字符串调用代码
    • symbol没有字面量,只要创建symbol实例作为object的新属性,就可以保证不会覆盖已经有的对象
    • symbol函数不能用new关键字作为构造函数使用,let mySymbol = new Symbol() // TypeError: Symbol is not a constructor
    • 使用symbol包装对象
      • let mySymbol = Symbol() let myWrappedSymbol = Object(mySymbol)
    • 例子
      1. let sym = Symbol() console.log(typeof sym) // symbol
      2. let genericSymbol = Symbol() let otherGenericSymbol = Symbol() let fooSymbol = Symbol('foo') let otherFooSymbol = Symbol('foo') console.log(genericSymbol == otherGenericSymbol) // false console.log(fooSymbol == otherFooSymbol) // false
  • 使用全局符合注册表
    • 使用Symbol.for() 创建共享符号
    • 过程:搜索全局符号注册表,(幂等操作),若存在,就返回符号实例,不存在,创建一个新符号并添加注册到注册表中
      • let fooGlobalSymbol = Symbol.for('foo') // 创建新符号 let otherFooGlobalSymbol = Symbol.for('foo') // 重用已有符号
    • 使用Symbol().for()注册的和Symbol()函数注册的不相同
      • let localSymbol = Symbol('foo') let globalSymbol = Symbol.for('foo') console.log(localSymbol === globalSymbol); // false
    • 全局注册表中的符号必须是string,任何参数通过Symbol.for(),都会转换为string
    • Symbol.keyFor()查询全局注册表
      • 参数必须是symbol,
      • 返回全局符合对应的字符串,如没有则是undefined
      // 创建全局符号
      let s = Symbol.for('foo')
      console.log(Symbol.keyFor(s))   // foo
      // 创建普通符号
      let s2 = Symbol('bar')
      console.log(Symbol.keyFor(s2) // undefined
      //传给Symbol.keyFor()的不是符号
      Symbol.keyFor(123); // TypeError: 123 is not a symbol
      
  • 使用符号作为属性
    • 凡是可以用字符串和数值作为属性的地方。也可以使用符合
    • 对象字面量属性和Object.defineProperty()和Object.defineProperties()定义属性
      • Object.defineProperty()
        1. 添加、修改对象属性,通过属性特征描述符可以控制属性的可配置性,可枚举性,可修改性
        2. Object.defineProperty(obj, props, descriptor),返回修改的目标对象
          obj:要定义或者修改的对象
          props: 属性名
          descriptor: 属性描述
          descriptor: {
            configurable: false  // 可配置性
            enumerable: false // 可枚举性
            writable: true // 可修改性
            value: '' // 定义属性值 方式一
            get() { // 定义属性值 方式二
        
            },
            set() { // 定义属性值 方式二
        
            }
          }
        
        方式二与方式一不能同时出现 3. 例子
        let s1 = Symbol('foo'),
            s2 = Symbol('bar'),
            s3 = Symbol('baz'),
            s4 = Symbol('qux');
        let o = {
          [s1]: 'foo val'
        };
        let a = Object.defineProperty(o, s2, {value: 'bar val'})
        console.log(o) // {Symbol(foo): 'foo val', Symbol(bar): 'bar val'}
        console.log(a) // {Symbol(foo): 'foo val', Symbol(bar): 'bar val'}
        
      • Object.defineProperties()
        1. 一次性定义多个属性
        2. Object.defineProperty(obj, descriptor),返回修改的目标对象,两个参数
          obj:要定义或者修改的对象
          descriptor:属性集合对象
          descriptor:{
            name: {
              value: 'key',
              configurable: false  // 可配置性
              enumerable: false // 可枚举性
              writable: true // 可修改性
            }
          }
        
        // 例子
         let s1 = Symbol('foo'),
             s2 = Symbol('bar'),
             s3 = Symbol('baz'),
             s4 = Symbol('qux');
         let o = {
           [s1]: 'foo val'
         };
         let b = Object.defineProperties(o, {
             [s3]: {value: 'baz val'},
             [s4]: {value: 'qux val'}
         })
         console.log(o) // {Symbol(foo): 'foo val', Symbol(baz): 'baz val', Symbol(qux): 'qux val'}
         console.log(b) // {Symbol(foo): 'foo val', Symbol(baz): 'baz val', Symbol(qux): 'qux val'}
        
      • Object.getOwnPropertyNames()
        1. 返回对象实例的常规属性数组
      • Object.getOwnPropertySymbols()
        1. 返回对象实例的符号属性数组
      • Object.getOwnPropertyDescriptors()
        1. 返回同时包含常规和符号属性描述符的对象
      • Reflect.ownKeys()
        1. 返回两种类型的键
      • 例子
        let s1 = Symbol('foo'),
            s2 = Symbol('bar');
        let o = {
          [s1]: 'foo val',
          [s2]: 'bar val',
          baz: 'baz val',
          qux: 'qux val'
        };
        console.log(Object.getOwnPropertySymbols(o));
        // [Symbol(foo), Symbol(bar)]
        
        console.log(Object.getOwnPropertyNames(o));
        // ["baz", "qux"]
        
        console.log(Object.getOwnPropertyDescriptors(o));
        // {baz: {...}, qux: {...}, Symbol(foo): {...}, Symbol(bar): {...}}
        // {baz: {configurable: true, enumerable: true, value: "baz val", writable: true}}
        
        console.log(Reflect.ownKeys(o));
        // ["baz", "qux", Symbol(foo), Symbol(bar)]
        
      • 查找没有显示保存属性的引用值时,要循环去寻找
          let o = {
            [Symbol('foo')]: 'foo val',
            [Symbol('bar')]: 'bar val'
          };
        
          console.log(o);
          // {Symbol(foo): "foo val", Symbol(bar): "bar val"}
        
          let barSymbol = Object.getOwnPropertySymbols(o)
                        .find((symbol) => symbol.toString().match(/bar/));
        
          console.log(barSymbol);
          // Symbol(bar)
        
  • 常用内置符号
    • es6引入了常用的内置符号,暴露语言内部行为,可以让开发者直接访问、重新或者模拟这些行为
    • 都是一symbol工厂函数字符串属性的形式存在
    • 用途之一重新定义,改变原生结构
    • 是全局函数Symbol的普通字符串属性,指向一个符号的实例
    • 内置符号属性都是不可写、不可枚举、不可配置的
    • 引用符号的前缀是@@ @@iterator 指的就是Symbol.iterator
  • symbol.asyncIterator
    • 实现异步迭代器API的函数
    • 有for-await-of 语句使用
    • 例子
        class Emitter {
          constructor(max) {
            this.max = max;
            this.asyncIdx = 0;
          }
      
          async *[Symbol.asyncIterator]() {
            while(this.asyncIdx < this.max) {
              yield new Promise((resolve) => resolve(this.asyncIdx++));
            }
          }
        }
        async function asyncCount() {
          let emitter = new Emitter(5);
      
          for await(const x of emitter) {
            console.log(x);
          }
        }
        asyncCount();
          // 0
          // 1
          // 2
          // 3
          // 4
      
  • Symbol.hasInstance
    • instanceof 确认一个对象实例的原型链上是否有原型
      • 例子
          function Foo() {}
          let f = new Foo();
          console.log(f instanceof Foo);
        
    • ES6中 instanceof 操作符使用 Symbol.hasInstance函数确认关系
      • 以Symbol.hasInstance为key的函数也会执行,区别是操作数对调
      • 例子
          function Foo() {}
          let f = new Foo();
          console.log(Foo[Symbol.hasInstance](f)); // true
        
      • 属性定义在Function原型上,因此默认所有的函数和类上都能调用。
      • instanceof会在原型上寻找这个属性定义,可以在继承的类上通过静态方法重新定义
      • 例子
          class Bar {}
          class Baz extends Bar {
            static [Symbol.hasInstance]() {
              return false;
            }
          }
          let b = new Baz();
          console.log(Bar[Symbol.hasInstance](b)); // true
          console.log(b instanceof Bar);           // true
          console.log(Baz[Symbol.hasInstance](b)); // false
          console.log(b instanceof Baz);           // false
        
  • Symbol.isConcatSpreadable
    • 通过Symbol.isConcatSpreadable的值(false、true)修改ES6concat()方法
    • 为false、假值的时候整个对象被加到数组末尾
      • 例子
      let initial = ['foo'];
      let array = ['bar'];
      console.log(initial.concat(array)); // ['foo', 'bar']
      console.log(array[Symbol.isConcatSpreadable]) // undefined
      array[Symbol.isConcatSpreadable] = false
      console.log(initial.concat(array));  // ['foo', Array(1)]
      
    • 为true、真值把这个类数组对象打平到数组实例
      • 类数组对象
      let say = function () {
        console.log(arguments) // 类数组对象 不能用数组的方法,但是可以用索引来访问。存在length
      }
      say(1, 2)
      // 结构为
      0: 1
      1: 2
      length: 2
      {0: 1, 1: 2, length: 2}
      如果一个普通的对象加上一个 length 属性就可以变成一个类数组对象。
      {length: 1, value: 1, 0: 3}
      
      • 讲类数组对象转换为数组
      let say = function () {
        console.log(Array.from(arguments)) // [1, 2]
      }
      say(1, 2)
      
      • 例子
      let initial = ['foo'];
      let arrayLikeObject = { length: 1, 0: 'baz' };
      console.log(initial.concat(arrayLikeObject)); // ['foo', {…}]
      console.log(arrayLikeObject[Symbol.isConcatSpreadable]); // undefined
      arrayLikeObject[Symbol.isConcatSpreadable] = true;
      console.log(initial.concat(arrayLikeObject)); // ['foo', 'baz']
      
    • 不是类数组对象的在设置为true时会被忽略
      • 例子
      let initial = ['foo'];
      let otherObject = new Set().add('qux');
      console.log(initial.concat(otherObject)); // ['foo', Set(1)]
      console.log(otherObject[Symbol.isConcatSpreadable]);  // undefined
      otherObject[Symbol.isConcatSpreadable] = true;
      console.log(initial.concat(otherObject));             // ['foo']
      
  • Symbol.iterator
    • 表示实现迭代器API的函数
    • 有for-of语句使用
    • 默认这个函数会返回一个实现迭代器API的对象,返回的这个对象是实现该API的generator
    • 例子
      class Emitter {
        constructor(max) {
          this.max = max;
          this.idx = 0;
        }
        *[Symbol.iterator]() {
          while(this.idx < this.max) {
            yield this.idx++;
          }
        }
      }
      function count () {
        const emitter = new Emitter(5);
        for (const x of emitter) {
            console.log(x);
          }
      }
      count() // 0, 1, 2, 3, 4
      
  • Symbol.match
    • 表示一个正则表达式方法,用正则表达式去匹配字符串
    • 由String.prototype.macth()调用
    • 参数是一个正则表达式,若不是正则,会转换成正则对象
    • 可以自定义Symbol.match函数
    • 例子
    class FooMatcher {
      static [Symbol.match](target) {
        return target.includes('foo');
      }
    }
    console.log('foobar'.match(FooMatcher)); // true
    console.log('barbaz'.match(FooMatcher)); // false
    class StringMatcher {
      constructor(str) {
        this.str = str;
      }
    
      [Symbol.match](target) {
        return target.includes(this.str);
      }
    }
    console.log('foobar'.match(new StringMatcher('foo'))); // true
    console.log('barbaz'.match(new StringMatcher('qux'))); // false
    
  • Symbol.replace
    • 替换一个字符串中匹配的子串
    • 由String.prototype.replace()调用
    • 参数是一个正则表达式,若不是正则,会转换成正则对象
    • 可以自定义Symbol.replace函数
    class FooReplacer {
      static [Symbol.replace](target, replacement) {
        return target.split('foo').join(replacement);
      }
    }
    
    console.log('barfoobaz'.replace(FooReplacer, 'qux'));  // "barquxbaz"
    class StringReplacer {
      constructor(str) {
        this.str = str;
      }
    
      [Symbol.replace](target, replacement) {
        return target.split(this.str).join(replacement);
      }
    }
    
    console.log('barfoobaz'.replace(new StringReplacer('foo'), 'qux')); // "barquxbaz"
    
  • Symbol.search
    • 返回匹配字符串中子串的下标
    • 由String.prototype.search()调用
    • 参数是一个正则表达式,若不是正则,会转换成正则对象
    • 可以自定义Symbol.search函数
    class FooSearcher {
        static [Symbol.search](target) {
          return target.indexOf('foo');
        }
      }
      console.log('foobar'.search(FooSearcher)); // 0
      console.log('barfoo'.search(FooSearcher)); // 3
      console.log('barbaz'.search(FooSearcher)); // -1
    
      class StringSearcher {
        constructor(str) {
          this.str = str;
        }
    
        [Symbol.search](target) {
          return target.indexOf(this.str);
        }
      }
    
      console.log('foobar'.search(new StringSearcher('foo'))); // 0
      console.log('barfoo'.search(new StringSearcher('foo'))); // 3
      console.log('barbaz'.search(new StringSearcher('qux'))); // -1
    
  • Symbol.species
    • 是一个函数值的属性,被构造函数用以创建派生对象
    • 用Symbol.species 定义静态的获取器(getter)方法, 可以修改子类覆盖对象的默认构造函数
    class Bar extends Array {}
    let bar = new Bar();
    console.log(bar instanceof Array); // true
    console.log(bar instanceof Bar);   // true
    bar = bar.concat('bar');
    console.log(bar instanceof Array); // true
    console.log(bar instanceof Bar);   // true
    class Baz extends Array {
      // 覆盖 species 到父级的 Array 构造函数上
      static get [Symbol.species]() {
        return Array;
      }
    }
    let baz = new Baz();
    console.log(baz instanceof Array); // true
    console.log(baz instanceof Baz);   // true
    baz = baz.concat('baz');
    console.log(baz instanceof Array); // true
    console.log(baz instanceof Baz);   // false
    
  • Symbol.split
    • 在匹配正则表达式的索引位置拆分字符串
    • String.prototype.split()方法使用
    • 参数是一个正则表达式,若不是正则,会转换成正则对象
    • 可以自定义Symbol.split函数
    class FooSplitter {
      static [Symbol.split](target) {
        return target.split('foo');
      }
    }
    
    console.log('barfoobaz'.split(FooSplitter));  // ["bar", "baz"]
    class StringSplitter {
      constructor(str) {
        this.str = str;
      }
    
      [Symbol.split](target) {
        return target.split(this.str);
      }
    }
    console.log('barfoobaz'.split(new StringSplitter('foo'))); // ["bar", "baz"]
    
  • Symbol.toPrimitive
    • 是一个方法,将对象转换为相应的原始值
    • 由ToPrimitive 抽象操作使用
    • 自定义的对象,实例可以通过Symbol.toPrimitive,定义一个函数改变默认行为
    class Foo {}
    let foo = new Foo();
    
    console.log(3 + foo);       // "3[object Object]"
    console.log(3 - foo);       // NaN
    console.log(String(foo));   // "[object Object]"
    class Bar {
      constructor() {
        this[Symbol.toPrimitive] = function(hint) {
          // hint 为number、string、default
          switch (hint) {
            case 'number':
              return 3;
            case 'string':
              return 'string bar';
            case 'default':
            default:
              return 'default bar';
          }
        }
      }
    }
    let bar = new Bar()
    console.log(3 + bar);     // "3default bar“
    console.log(3 - bar);    // 0
    console.log(3 - bar);    // string bar
    
  • Symbol.toStringTag
    • 通常作为对象的属性key使用
    • 对应的数值应该为string类型,用来表示该对象的自定义类型标签
    • 通常由内置的 Object.prototype.toString() 调用,把它包含在自己的返回值
    • Object.prototype.toString() 调用时,会寻找symbol.toStringTag指定的实例标识符。默认为Object
    • 自定义类实例需要明确定义
    let s = new Set()
    console.log(s) // Set(0) {}
    console.log(s.toString()) // [object Set]
    console.log(s[Symbol.toStringTag]) // Set
    
    class Foo = {}
    let foo= new Foo()
    console.log(foo);                      // Foo {}
    console.log(foo.toString());           // [object Object]
    console.log(foo[Symbol.toStringTag])  // undefined
    
    class Bar {
      constructor() {
        this[Symbol.toSrtingTag] = 'Bar'
      }
    }
    let bar = new Bar()
    console.log(bar);                      // Bar {}
    console.log(bar.toString());           // [object Bar]
    console.log(bar[Symbol.toStringTag])  // Bar
    
  • Symbol.unscopables
    • 指用于指定对象值,其对象自身和继承的从关联对象的 with 环境绑定中排除的属性名称
    • 表示一个对象所有的以及集成的属性,从关联对象的with环境绑定中排除
    • 不推荐使用
    let o = { foo: 'bar'}
    with(o) {
      console.log(foo) // bar
    }
    o[Symbol.unscopables] = {
      foo: true
    }
    with(o) {
      console.log(foo) // foo is not defined
    }
    

Object

  • 一组数据和功能的集合
  • 通过创建object类型的实例来创建自己的对象
    let o = new Object()
    
  • 每个object的实例都有下面的属性和方法
    • constructor
      • 用于创建和初始化class创建的对象的特殊方法
      class Bar {
        constructor() {
          this.name = 'bar'
        }
      }
      const bar = new Bar()
      console.log(bar.name) // 'bar'
      
    • hasOwnProperty
      • 判断当前对象实例(不是原型)上,是否存在给定的属性
      • 参数必须是string或者symbol
      const foo = {}
      foo.name = 'foo'
      console.log(foo.hasOwnProperty(name)) // true
      console,log(foo.hasOwnProperty(sex)) // false
      
    • isPrototypeOf
      • 判断一个对象是否存在另一个对象的原型连上
      function Foo() {}
      function Bar() {}
      Bar.prototype = Object.create(Foo.prototype)
      const bar = new Bar()
      Bar.prototype.isPrototypeOf(bar) // true
      Foo.prototype.isPrototypeOf(bar) // true
      
    • propertyIsEnumerable
      • 判断当前给的属性可不可以使用for-in枚举
      const o = {}
      const a = []
      o.name = 'object'
      a[0] = 'array'
      console.log(a.propertyIsEnumerable(0)) // true
      console.log(o.propertyIsEnumerable('name')) // true
      console.log(a.propertyIsEnumerable('length)) // false
      console.log(o.isPrototypeOf('isPrototypeOf')) // false
      
    • toLocaleString
      • 返回对象的字符串表示,反应对象所在的本地化执行环境
      • 主要用于将数组 Number对象或Date对象转换为本地格式的字符串
    • toString
      • 返回对象的字符串表示
      • 与toLocaleString区别
      const a = 123
      console.log(a.toString()) // '123'
      console.log(a.toLocaleString()) '123'
      const b = 1234567
      console.log(b.toString()) // '1234567'
      console.log(b.toLocaleString()) '1,234,567'
      const time = new Date()
      console.log(time.toString()) // Wed Dec 29 2021 12:59:04 GMT+0800 (中国标准时间)
      console.log(time.toLocaleString()) // 2021/12/29 下午12:59:04
      
    • valueOf
      • 返回对象对应的字符串、数值、布尔值
      const num = 123456
      console.log(num.valueOf()) // 123456
      const array = ["ABC", true, 12, -5]
      console.log(array.valueOf() === array) // true
      const date = new Date(2021, 12, 29, 13, 11, 59, 230)
      console.log(date.valueOf())   // 1643433119230