ES6 学习笔记(八)基本类型Symbol

发布于 2021-05-22  319 次阅读


1、前言

大家都知道,在ES5的时候JavaScript的基本类型有Number、String、Boolean、undefined、object、Null共6种,在es6中,新增了Symbol类型,用于表示独一无二的值。之后又提出了bigInt类型(前面提到过),这里简要的总结Symbol的一些基本用法。

2、基本用法

2.1 用法一(直接使用)

可以直接使用Symbol()创建新的symbol类型,如:

let s = Symbol()
console.log(typeof s); // symbol

注意:
Symbol函数前不能使用new命令,因为生成的Symbol是一个原始类型的值,不是对象。如:

let s =  new Symbol("123")
console.log(s);

结果是什么都没输出。

2.2 用法二(接收参数)

Symbol函数可以接收一个字符串作为它的参数,表示对该实例的描述。如:

let s = Symbol("foo")
console.log(typeof s); // symbol

若Symbol函数的参数时一个对象,则会调用它的toString()方法,如:先转化成字符串,再生成一个Symbol值。如:

const obj = {
    toString() {
        return 'abc'
    }
}
const sym = Symbol(obj)
console.log(sym); // Symbol(abc)

由于每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;因此下面的结果都是false。

let s1 = Symbol()
let s2 = Symbol()
let s3 = Symbol(undefined)
let s4 = Symbol(undefined)
console.log("s1==s2", s1 == s2); // s1==s2 false
console.log("s3==s4", s3 == s4); // s3==s4 false
console.log(Symbol("foo") == Symbol("foo")); // false
  • 注意:
    Symbol值不能与其他类型的值进行运算,否则会报错。
    Symbol值可以显式的转换为字符串。
    Symbol值可以显式的转换为布尔值,但不能转换为数值。

2.3 用法三(作为属性名)

Symbol作为对象属性名保证不会出现同名的属性。对于对象中的键名十分有效,能够保证他们不被修改。如:

let proSym = Symbol()
let obj = {}
obj.proSym = "hello" // 作为数据属性使用

console.log(obj[proSym]); // undefined
console.log(obj['proSym']); // hello

注意:
当Symbol作为属性名时不能使用点运算符。
在对象内部使用Symbol值定义属性时,Symbol值必须放在方括号中。

2.4 用法四(作为方法)

Symbol 可用于定义方法

let proSym = Symbol()
let obj = {
    [proSym](name){
        console.log('hello,'+name);
    }
}
obj[proSym]("Marry") // hello,Marry

2.5 用法四(定义常量)

Symbol 可以用来定义一组常量,保证这组的常量值都不相等

const COLOR_RED = Symbol();
const COLOR_GREEN = Symbol();
function getComplements(color) {
    switch (color) {
        case COLOR_RED:
            console.log('color is red');
            break;
        case COLOR_GREEN:
            console.log('color is green');
            break;
        default:
            throw new Error('Undefined color');
    }
}
getComplements(COLOR_GREEN);
getComplements(123456);

输出结果:
color is green
Error: Undefined color

3、属性名遍历

Symbol作为对象中的属性名时,可以通过Object.getOwnPropertySymbols()方法获取。该方法返回的是一个数组,包含当前对象所有用做属性名的Symbol值。如:

let obj = {}
let a = Symbol('a')
let b = Symbol('b')
obj[a] = 'Hello'
obj[b] = 'World'
let objectSymbols = Object.getOwnPropertySymbols(obj) 
console.log(objectSymbols); 

输出结果:
[ Symbol(a), Symbol(b) ]

另外的几种方法:

  • Object.defineProperty() 直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
  • Object.getOwnPropertyNames() 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
let obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';

let foo = Symbol('foo');
Object.defineProperty(obj, foo, {
    value: 'foobar'
});
for (let i in obj) {
    console.log(i);
}
let r1 = Object.getOwnPropertyNames(obj);
console.log(r1);
let r2 = Object.getOwnPropertySymbols(obj);
console.log(r2);

输出结果:
{ [Symbol(a)]: 'Hello', [Symbol(b)]: 'World' }
[]
[ Symbol(a), Symbol(b), Symbol(foo) ]

4、Reflect.ownKeys()方法

Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和Symbol键名。如:

let obj = {
    [Symbol('my key')]: 1,
    enum: 2,
    notenum: 3
}
let s= Reflect.ownKeys(obj)
console.log(s);

输出结果:
[ 'enum', 'notenum', Symbol(my key) ]

5、Symbol.for()与Symbol.keyFor()方法

1、Symbol.for(key)方法会根据给定的key从symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该key相关联的 symbol,并放入全局 symbol 注册表中。如:

let s1 = Symbol.for("foo");
let s2 = Symbol.for("foo");
console.log(s1 == s2);

上面的s1指的是创建一个键名为foo的Symbol放入Symbol注册表中,s2指的是从Symbol注册表中取出键名为foo的Symbol。两者是同一个,所以输出结果是true。

2、Symbol.keyFor(sym) 方法用来获取全局symbol 注册表中与某个 symbol 关联的键。若查找到该symbol,则返回该symbol的key值,否则返回undefined。如:

let s3 = Symbol.for("food");
console.log(Symbol.keyFor(s3));
console.log(Symbol.keyFor(Symbol.iterator));

输出结果:
food
undefined

6、消除魔术字符串

魔术字符串,是指在代码中多次出现且与代码形成强耦合的某一个具体的字符串或数值。为了尽量减少这种情况,应该由变量代替这些具体的字符串或数值。

6.1 实例1:计算三角形和正方形的面积

function getArea(shape, options) {
    let area = 0;
    switch (shape) {
        case 'Triangle':
            area = 0.5 * options.width * options.height;
            break;
        case 'Square':
            area = options.height ** 2;
            break;
        default:
            throw new Error('undefined shape');
    }
    return area;
}
let r1 = getArea('Triangle', { width: 100, height: 100 });
console.log(r1);
let r2 = getArea('Square', { width: 100, height: 100 });
console.log(r2);

运行结果:
5000
10000

可以看到,上面代码中的Triangle与Square都是魔术字符串,那么怎么消除它们呢

6.2 实例2:计算三角形和正方形的面积

通过变量来代替字符串,如:

let shapeType = {
    triangle: 'Triangle',
    square: 'Square'
};
function getArea(shape, options) {
    let area = 0;
    switch (shape) {
        case shapeType.triangle:
            console.log(shapeType.triangle);
            area = 0.5 * options.width * options.height;
            break;
        case shapeType.square:
            console.log(shapeType.square);
            area = options.height ** 2;
            break;
        default:
            throw new Error('undefined shape');
    }
    return area;
}

let r1 = getArea(shapeType.triangle, { width: 100, height: 100 });
console.log(r1);
let r2 = getArea(shapeType.square, { width: 100, height: 100 });
console.log(r2);

输出结果:
Triangle
5000
Square
10000

6.3 实例3:计算三角形和正方形的面积

还可以通过Symbol()代替字符串,如:

let shapeType = {
    triangle: Symbol(),
    square: Symbol()
}
function getArea(shape, options) {
    let area = 0;
    switch (shape) {
        case shapeType.triangle:
            console.log(shapeType.triangle);
            area = 0.5 * options.width * options.height;
            break;
        case shapeType.square:
            console.log(shapeType.square);
            area = options.height ** 2;
            break;
        default:
            throw new Error("undefind shape")
    }
    return area
}
let r1 = getArea(shapeType.triangle, { width: 100, height: 100 });
console.log(r1);
let r2 = getArea(shapeType.square, { width: 100, height: 100 });
console.log(r2);

输出结果:
Symbol()
5000
Symbol()
10000


活的像诗一样