重学ES6(二)

117 阅读4分钟

Symbol

symbol不能使用new命令,否则会报错。

这里有一个疑问,symbol和number,string之类的都是基本数据类型,但是可以new Number,new String,而不能new Symbol,为什么呢?

查阅资料后又得知一个名词“基本包装类型”

JS中的String,Number,Boolean都是基本包装类型,所谓包装类型就是当我们在创建这三种数据类型时,都会经过一层包装,从而能使用相应的属性和方法。

// 比如这里,虽然str是基本数据类型,但是也会有属性和方法
var str = 'hello'; //string 基本类型 
var s2 = str.charAt(0); //在执行到这一句的时候 后台会自动完成以下动作 : 
//后台偷偷发生的 var _str = new String('hello'); // 1.创建String类型的一个实例 
var s2 = _str.chaAt(0); // 2 在实例上调用指定的方法,并且返回结给s2 
_str = null; // 3.销毁这个实例 alert(s2);//h 
alert(str);//hello

str.pro = 'world'
console.log(str.pro); // undefined  
//所以这里的undefined就是给String类型一个pro属性,立马销毁,再次输出这个属性的时候当然是undefined了

参考链接JS中的基本包装类型 

null和undefined因为不需要原型属性和方法,所以也不需要包装类型

至于symbol,bigInt亦如symbol

围绕原始数据类型创建一个显式包装器对象从 ECMAScript 6 开始不再被支持。 然而,现有的原始包装器对象,如 new Booleannew String以及new Number,因为遗留原因仍可被创建。 -- MDN Symbol

可以用Object() 函数创建一个 Symbol 包装器对象

var sym = Symbol("foo");
typeof sym;     // "symbol"
var symObj = Object(sym);
typeof symObj;  // "object"

Symbol 值作为键名,不会被常规方法遍历得到,即Object.keys,for in...

1、通过Object.getOwnPropertySymbols()方法获取

const obj = {a: 1, b : 2, [Symbol('c')]: 3}
Object.keys(obj)  // ['a', 'b']
Object.getOwnPropertySymbols(obj)  // [Symbol(c)]

2、通过Reflect.ownKeys()

const obj = {a: 1, b : 2, [Symbol('c')]: 3}
Reflect.ownKeys(obj)  // ['a', 'b', Symbol(c)]

Symbol.for()

Symbol.for("bar") === Symbol.for("bar")
// true

Symbol("bar") === Symbol("bar")
// false

Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。

Symbol.for()具有全局登记特性,可以用在不同的 iframe 或 service worker 中取到同一个值。

React中为每个react element都添加了$$typeof: Symbol.for('react.element')这个属性来防治xss攻击

Set 和 Map 数据结构

利用set快速取并集(Union)、交集(Intersect)和差集(Difference)

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

weakSet和weakMap

const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}

a数组的成员成为 WeakSet 的成员,而不是a数组本身。这意味着,数组的成员只能是对象。

const b = [3, 4];
const ws = new WeakSet(b);
// Uncaught TypeError: Invalid value used in weak set(…)

上面代码中,数组b的成员不是对象,加入 WeakSet 就会报错。

具体使用场景可以参考这篇文章 19. Maps and Sets

Promise

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
  // 这里的代码依然可以执行,但是promise的状态不会再改变,所以不会执行catch
  throw Error('something wrong...');
}).then(r => {
  console.log(r);
}).catch(err => {
  console.log(err);
});

// 2
// 1

Iterator

给object添加Iterator的两种方式:

1、generator形式

let obj = {
  * [Symbol.iterator]() {
    yield 'hello';
    yield 'world';
  }
};

const objEntity = { a: 1, b: 2, c: 3 }

function* entries(objEntity) {
  for (let key of Object.keys(objEntity)) {
    yield [key, objEntity[key]];
  }
}

2、原生形式

obj[Symbol.iterator] = function () {
    let nextIndex = 0;
    const arr = Object.entries(this);
    return {
      next: function () {
        return nextIndex < arr.length
          ? {
              value: arr[nextIndex++],
              done: false,
            }
          : {
              done: true,
            };
      },
    };
};

Generator

Generator的this指向

Generator总是返回遍历器对象,而不像普通函数谁调用指向谁

function* g() {
  this.a = 11;
}

let obj = g();
obj.next();
obj.a // undefined

Generator不能new

async

错误处理

async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
  ...
}

如果希望下面的await或者代码块可以继续执行,可以在前面的await加上catch

async function f() {
  await Promise.reject('出错了').catch(e => console.log(e));
  await Promise.resolve('hello world');
  ...
}

写法优化

let foo = await getFoo();
let bar = await getBar();

上面的写法中,getBar必须要等到getFoo执行完毕,比较费时,可以用以下两种方法优化:

// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

Class

属性表达式

类的属性名,可以是表达式(不太明白什么场景会用到

let methodName = 'getArea';

class Square {
  constructor(length) {
    // ...
  }

  [methodName]() {
    // ...
  }
}

Module

import

注意,import命令具有提升效果,会提升到整个模块的头部,首先执行。

foo();

import { foo } from 'my_module';

上面的代码不会报错,因为import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。