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 Boolean
、new 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
命令是编译阶段执行的,在代码运行之前。