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

刚学习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的八种基础数据类型之一;主要的作用在于表示一个独一无二的标识;它可以作为对象的属性键来避免属性键的冲突,并且由于它的不可变性,可以作为对象属性的值来保护对象属性的完整性。

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax