基础数据类型,什么竟然还有不会基础数据类型?

刚学习js的时候就知道es6新增了symbol数据类型,如今工作快四年了,对于symbol也是一点也不了解,既然是这么基础的知识点,那么就一定需要重点学习一下的啦!✌🏻

symbol 是ECMAScript 6新增的一种基础数据类型。通过使用Symbol()构造函数创建唯一、不可变的实例。 主要用途是表示一个独一无二的标识符。

🚀symbol 的特点

  1. symbol的值是唯一且不可变的,可以用来定义不冲突的属性名或变量名。
  2. symbol数据类型不能与其他的数据类型进行运算;symbol可以显式转化为string类型和boolean类型,不能转化成number类型。
  3. symbol不能使用for...of或者for...in循环打印key值。可以使用Object.getOwnPropertySymbols()方法或Reflect.ownKeys()方法来获取。
  4. 使用Symbol创建symbol的时候不需要使用new关键字

🌈symbol 基本用法

当我们需要创建一个symbol类型的时候,需要使用Symbol()构造函数进行创建;因为symbol是基础数据类型,所以使用typeof可以识别symbol类型。

javascript 复制代码
let s = Symbol();
console.log(typeof s)   // symbol

Symbol()函数可以传入一字符串参数,作为symbol的描述,但是这个字符串参数与symbol定义或标识完全无关

javascript 复制代码
let s1 = Symbol();
let s2 = Symbol();

let y1 = Symbol('zifu');
let y2 = Symbol('zifu');

console.log(s1 == s2)   // false
console.log(y1 == y2)   // false

📝使用全局symbol注册表

如果需要复用symbol实例,那么可以用一个字符串作为键,在全局symbol注册表中创建并复用symbol。 当你多次调用Symbol.for()函数并传入相同的字符串键时,它会始终返回相同的Symbol值。使用某个字符串调用时,它会检查全局注册表,发现不存在对应的symbol,于是就会生产一个新的symbol实例并添加到注册表中。后续使用相同的字符串调用同样会检查注册表,发现存在该字符串对应的symbol实例就会返回该symbol实例。

javascript 复制代码
let s = symbol.for('one');  // 创建新symbol
let s2 = symbol.for('one'); // 重新使用已有的symbol
console.log( s === s2 );    // true

全局注册表中的symbol必须是使用字符串键来创建,因此作为参数传给Symbol.for()的任何值都会被转化为字符串。同时参数也会被用作symbol的描述。

javascript 复制代码
let a = Symbol.for('one')
console.log(a)  // Symbol(one)

Symbol.keyFor()函数可以用来查询symbol在全局注册表中所对应的字符串;如果不存在,则返回undefined。如果传值不是一个symbol,那么会抛出typeError

javascript 复制代码
let s = Symbol.for('one');
console.log(Symbol.keyFor(s)) // one

let s2 = Symbol('two');
console.log(Symbol.keyFor(s2)) // undefined

Symbol.keyFor(123)  // TypeError: 123 is not a symbol

使用symbol作为对象的属性

凡是可以使用数值和字符串作为属性的地方,都可以使用symbol。这就包含了对象字面量属性和Object.defaultProperty()/Object.definedProperties()定义的属性。

javascript 复制代码
// 第一种写法
let o = {
    [Symbol('one')]: 'first'
};
console.log(o);   // {Symbol(three): 'first'}

// 第二种写法
let o1 = {};
o1[Symbol('two')] = 'second'
console.log(o1);  // {Symbol(two): 'second'}

// 第三种写法
let o2 = {};
Object.defineProperty(o2, Symbol('three'), {value: 'third'});
console.log(o2);  // {Symbol(three): 'third'}

// 第四种写法
let o3 = {};
Object.defineProperties(o3, {
    [Symbol('four')]: {value: 'fourth'}
});
console.log(o3);  // {Symbol(four): 'fourth'}

在操作数组的时候 Object.getOwnPropertyNames() 函数会返回对象实例的常规属性数组,而不会返回对象实例的symbol属性数组;Object.getOwnPropertySymbols()函数则会返回对象实例symbol属性数组,不会返回对象实例的常规属性数组;如果要同时返回包含常规和symbol属性描述的对象,可以使用Object.getOwnPropertyDescriptors()函数;通过Reflect.ownKeys()函数会返回常规和symbol的键

javascript 复制代码
let obj = {
    [Symbol('one')]: 'first',
    [Symbol('two')]: 'second',
    three: 'third',
    four: 'fourth',
};

console.log(Object.getOwnPropertyNames(obj));       // ['three', 'four']
console.log(Object.getOwnPropertySymbols(obj));     // [Symbol(one), Symbol(two)]
console.log(Object.getOwnPropertyDescriptors(obj));  //four: {value: 'fourth', ...}three: {value: 'third', ...}Symbol(one): {value: 'first', ...}Symbol(two): {value: 'second', ...}
console.log(Reflect.ownKeys(obj));   // ['three', 'four', Symbol(one), Symbol(two)]

常用的内置symbol

symbol.asyncIterator

含义:一个方法,该方法返回对象默认的AsyncInerator。由for-await-of语句使用

表示实现异步迭代器API的函数。for-await-of循环会利用这个函数执行异步迭代器操作;循环过程中,会调用Symbol.asyncIterator为键的函数,并返回实现该API的异步生成器(AsyncGenerator)

javascript 复制代码
class Obj {
    async *[Symbol.asyncIterator()]() {};
}

let obj = new Obj();

console.log(obj[Symbol.asyncIterator]()); // AsyncGenerator {<suspended>}

通过这个Symbol.asyncIterator 函数生成的对象通过其 next() 方法可以陆续的返回promise()实例。通过显式调用next()方法和隐式地通过异步生成器函数返回

javascript 复制代码
class OBJ {
    constructor(max) {
        this.max = max;
        this.asyncIndex = 0;
    }
    async *[Symbol.asyncIterator]() {
        while(this.asyncIndex < this.max) {
            yield new Promise((resolve) => resolve(this.asyncIndex++))
        }
    }
}

async function asyncCount() {
    let obj = new OBJ(5);
    for await(const x of obj) {
        console.log(x);
    }
}

asyncCount()
// 0
// 1
// 2
// 3
// 4

symbol.hasInstance

含义:一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例。由instanceof操作符使用

这个应该不需要解释😜,想想instanceof关键字

javascript 复制代码
function One() {}
let o = new One();
console.log(o instanceof One); // true

class Car {};
let c = new Car();
console.log(c instanceof Car); // true

instanceof原理:当我们使用instanceof关键字的时候,会使用Symbol.hasInstance 函数来确定关系。以 Symbol.hasInstance 为键的函数会执行同样的操作,只是操作数对调了一下

javascript 复制代码
function One() {}
let o = new One();
console.log(One[Symbol.hasInstance](o)); // true

class Car {};
let c = new Car();
console.log(Car[Symbol.hasInstance](c)); // true

因为这个属性是定义在Function的原型上,所以默认在所有的函数和类上都可以调用。由于instanceof操作符会在原型链上寻找到这个属性定义,就跟在原型链上寻找其他属性一样,因此可以在继承的类上通过静态方法重写这个函数

javascript 复制代码
class Car{}
class Car1 extends Car {
    static [Symbol.hasInstance]() {
        return false;
    };
};
let benci = new Car1();
console.log(Car[Symbol.hasInstance](benci));   // true
console.log(benci instanceof Car);           // true
console.log(Car1[Symbol.hasInstance](benci));  // false
console.log(benci instanceof Car1);          // false

Symbol.isConcatSpreadable

含义:作用于Array.prototype.concat()方法上,表示一个布尔值,如果是true则展开数组元素,默认的情况下展开,值是undefined

就是专门用于Array.prototype.concat()方法的,我的理解是:isConcatSpreadable表示一个开关,默认展开合并,当值为true的时候也是展开合并,为false的时候就将concat的值作为一个元素加入到数组中

js 复制代码
let initial = ['张三'];

let array = ['李四'];
console.log(array[Symbol.isConcatSpreadable]);  // undefined
console.log(initial.concat(array));             // ['张三', '李四']
array[Symbol.isConcatSpreadable] = false;
console.log(initial.concat(array));             // ['张三', Array(1)]
array[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(array));             // ['张三', '李四']

上面的案例中可以看到当isConcatSpreadable等于undefined 或者 true的时候,合并两个数组时,是将两个数组中的元素放在了一起,当isConcatSpreadable等于false的时候,合并两个数组将数组作为一个元素合并在了一起。

Symbol.iterator

含义:一个方法,该方法返回对象默认的迭代器。由for-of语句使用。也就是表示实现迭代器API的函数

js 复制代码
class Obj {
    constructor(max) {
        this.max = max;
        this.idx = 0;
    }
    *[Symbol.iterator]() {
        while(this.idx < this.max) {
            yield this.idx++
        }
    }
}

function count() {
    let obj = new Obj(5);
    for (const x of obj) {
        console.log(x)
    }
}
count();
// 0
// 1
// 2
// 3
// 4

定义一个可以迭代的对象,实现Symbol.iterator方法,使用for...of等对象循环的时候就会执行Symbo.iterator重写的方法

Symbol.match

含义:一个正则表达式方法,该方法用正则表达式去匹配字符串。由String.prototype.match()使用

这个方法可以重写对于正则表达式的实现,增加个性化的逻辑

js 复制代码
class Matcher {
    static [Symbol.match](target) {
        return target.includes('abc');
    }
}

console.log('abcdefg'.match(Matcher)); // true
console.log('hijklmn'.match(Matcher)); // false

Symbol.replace

含义:一个正则表达式方法,该方法替换一个字符串中匹配的子串。由String.prototype.replace()使用

js 复制代码
class Replacer {
    static [Symbol.replace](target, replacement) {
        return target.split('abc').join(replacement);
    }
}
console.log('abcdefg'.replace(Replacer, 'zzz')) // zzzdefg

Symbol.search

含义:一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由String.prototype.search()方法使用

js 复制代码
class Searcher {
    static [Symbol.search](target) {
        return  target.indexOf('abc');
    }
}
console.log('abcdefg'.search(Searcher))  // 0

Symbol.species

含义:一个函数值,该函数作为创建派生对象的构造函数

js 复制代码
class MyArray extends Array {
  static get [Symbol.species]() {
    return Array;
  }
}
var a = new MyArray(1, 2, 3);
var mapped = a.map((x) => x * x);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true

Symbol.split

含义:一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由String.prototype.split()方法使用

js 复制代码
class Splitter {
    static [Symbol.split](target) {
        return target.split('abc');
    }
}
console.log('deabcfg'.split(Splitter)); // ['de', 'fg']

Symbol.toPrimitive

含义:一个方法,该方法将对象转换为相应的原始值。由ToPrimitive抽象操作使用。

js 复制代码
var obj1 = {};
console.log(+obj1); // NaN 
console.log(`${obj1}`); // "[object Object]" 
console.log(obj1 + ''); // "[object Object]" 
 
var obj2 = { 
    [Symbol.toPrimitive](hint) { 
        if (hint == 'number') { 
            return 10; 
        } 
        if (hint == 'string') {
            return 'hello'; 
        } 
        return true; 
    } 
};
console.log(+obj2); // 10 -- hint is "number" 
console.log(`${obj2}`); // "hello" -- hint is "string" 
console.log(obj2 + ''); // "true" -- hint is "default"

Symbol.toStringTag

含义:一个字符串,该字符串用于创建对象的默认字符串描述。有内置方法Object.prototype.toString()使用

js 复制代码
let s = new Set();

console.log(s); // Set(0) {}
console.log(s.toString()); // [object Set]
console.log(s[Symbol.toStringTag]); // Set

class Demo {}
let demo = new Demo();
console.log(demo); // Demo {}
console.log(demo.toString()); // [object Object]
console.log(demo[Symbol.toStringTag]); // undefined

class Demo1 {
    constructor() {
        this[Symbol.toStringTag] = 'Demo1';
    }
}
let demo1 = new Demo1();
console.log(demo1); // Demo1 {Symbol(Symbol.toStringTag): 'Demo1'}
console.log(demo1.toString()); // [object Demo1]
console.log(demo1[Symbol.toStringTag]); // Demo1

Symbol.unscopables

含义:一个对象,该对象所有的以及继承的属性,都会从关联的对象with环境绑定中排除

js 复制代码
let o = {
    name: '张三'
}
with(o) {
    console.log(name); //张三
}
o[Symbol.unscopables] = {
    name: true
}
with(o) {
    console.log(name); // ReferenceError
}

一般不推荐使用with关键字,所以也不要使用Symbol.unscopables

总结一下

Symbol是js的八种基础数据类型之一;主要的作用在于表示一个独一无二的标识;它可以作为对象的属性键来避免属性键的冲突,并且由于它的不可变性,可以作为对象属性的值来保护对象属性的完整性。

相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui