ES6随手记(持续)

  • start date: 2018-08-27 13:00:45

1 对象设置变量键值

难以形容,直接上代码:

1
2
3
4
5
6
7
let key = 'testkey';
let obj = {
key1: 'value1',
[key]: 'testvalue'
};

console.log(obj.testkey); // 'testvalue'

babel编译:

1
2
3
4
5
6
7
8
var _obj;

var key = 'testkey';
var obj = (_obj = {
key1: 'value1'
}, _obj[key] = 'testvalue', _obj);

console.log(obj.testkey); // 'testvalue'

2 String、Array匹配查找

String.prototype.includes()、Array.prototype.includes()方法。mdn
注:babel不能编译,移动端兼容:ios9及以上、安卓5及以上;PC:除IE外。

1
2
3
4
5
6
7
8
9
10
11
let str = 'abcdef';

console.log(str.includes('cd')); // true
console.log(str.includes('fg')); // false

let arr = [1, 2, 3, {c: 1}, 'ddd', NaN];
console.log(arr.includes(3)); // true
console.log(arr.includes(5)); // false
console.log(arr.includes({c: 1})); // false,注意
console.log(arr.includes('ddd')); // true
console.log(arr.includes(NaN)); // true

语法

arr.includes(searchElement [, fromIndex])

  • searchElement:需要查找的元素值。
  • fromIndex(可选):从该索引处开始查找 searchElement。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜索。默认为 0。

3 使用对象解构来解析字符串数组

例:

1
2
const message = '1994,Micheal Wayne,Chine,michealwayne@163.com';
const { 2: country, 4: email } = message.split(',');

babel编译:

1
2
3
4
5
var message = '1994,Micheal Wayne,Chine,michealwayne@163.com';

var _message$split = message.split(','),
country = _message$split[2],
email = _message$split[4];

4 还是那道题,变量a、b交换

1
2
3
4
5
6
7
let a = 1,
b = 2;

[ a, b ] = [ b, a ];

console.log(a); // 2
console.log(b); // 1

ps,上面代码babel结果:

1
2
3
4
5
6
7
8
9
10
var a = 1,
b = 2;

var _ref = [b, a];
a = _ref[0];
b = _ref[1];


console.log(a); // 2
console.log(b); // 1

5 数组concat()的另一种解决方式

1
2
3
4
5
let arr1 = [1, 2, 3],
arr2 = [4, 5, 6, 7];

let arr3 = [...arr1, ...arr2];
console.log(arr3); // [1, 2, 3, 4, 5, 6, 7]

ps,上面代码babel结果:

1
2
3
4
5
var arr1 = [1, 2, 3],
arr2 = [4, 5, 6, 7];

var arr3 = [].concat(arr1, arr2);
console.log(arr3); // [1, 2, 3, 4, 5, 6, 7]

6 ES6模块加载

1
2
3
4
5
6
7
8
9
10
11
// a.js (CommonJS)
module.exports = {
name: 'Micheal',
age: 18
};

// 等于
export default {
name: 'Micheal',
age: 18
}

引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 方法1
import info from './a'; // info = { name: 'Micheal', age: 18 }

// 方法2
import {default as info} from './a'; // info = { name: 'Micheal', age: 18 }

// 方法3
import * as info from './a';
info.default;
/* info = {
* get default () { return module.exports; }
* get name () { return this.default.name }.bind(info)
* get age () { return this.default.age }.bind(info)
* }
*/

7 利用...运算符合并两个Object


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let obj1 = {
a: 1
};
let obj2 = {
b: 2,
c: [1, 2, 3]
};
let obj3 = {
a: 3,
d: 4
};

let res = {
...obj1,
...obj2,
...obj3
};

console.log(res)

babel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 实现拼接
var n = Object.assign ||
function(e) {
for (var t = 1; t < arguments.length; t++) {
var r = arguments[t];
for (var n in r) Object.prototype.hasOwnProperty.call(r, n) && (e[n] = r[n])
}
return e
}

var obj1 = {
a: 1
};
var obj2 = {
b: 2,
c: [1, 2, 3]
};
var obj3 = {
a: 3,
d: 4
};

var res = n({},
obj1,
obj2,
obj3);
console.log(res)

8 利用...运算符展开字符串


1
2
let str = 'abcdefg';
console.log(...str); // 'a' 'b' 'c' 'd' 'e' 'f' 'g'

*这种方式还能避免js将32位Unicode识别为两个字符。如

1
2
3
let str = '\uD83D\uDE80y';
console.log(str.length); // 3
console.log([...str].length); // 2

9 利用...运算符求极值


1
2
3
let nums = [1, 3, 5, 7, 9, 11, 13]

Math.max(…nums) // 13

10 JavaScript 中默认为(或提供)iterable 的标准内建值包括:

  • Arrays
  • Strings
  • Generators
  • Collections / TypedArrays

11 被忽视的Symbol

Symbol作为ES6新出的一种新的基本类型,我显然忽视了它。

每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的

语法:

1
Symbol([description])

其中description 可选,是symbol的描述,可用于调试但不能访问symbol本身。

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

属性

  • length:Symbol.length为0;
  • prototype:描述symbol构造函数的原型。

除了自己创建的symbol,JavaScript还内建了一些在ECMAScript 5 之前没有暴露给开发者的symbol,它们代表了内部语言行为。它们可以使用以下属性访问:

  • Symbol.iterator:一个返回一个对象默认迭代器的方法。被 for...of 使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var str = 'abc';
    var ite = str[Symbol.iterator]();

    ite.next().value; // 'a'
    ite.next().value; // 'b'
    ite.next().value; // 'c'


    var myIterable = {}
    myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
    };
    [...myIterable] // [1, 2, 3]
  • Symbol.asyncIterator:一个返回对象默认的异步迭代器的方法。被 for await of 使用。

  • 正则表达式 symbols:Symbol.match、Symbol.replace、Symbol.search、Symbol.split
  • Symbol.hasInstance:一个确定一个构造器对象识别的对象是否为它的实例的方法。被 instanceof 使用。

    1
    2
    3
    4
    5
    6
    class MyArray {  
    static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
    }
    }
    console.log([] instanceof MyArray); // true
  • Symbol.isConcatSpreadable:一个布尔值,表明一个对象是否应该flattened为它的数组元素。被 Array.prototype.concat() 使用。

    1
    2
    3
    4
    5
    6
    7
    var alpha = ['a', 'b', 'c'], 
    numeric = [1, 2, 3];

    numeric[Symbol.isConcatSpreadable] = false;
    var alphaNumeric = alpha.concat(numeric);

    console.log(alphaNumeric); // 结果: ['a', 'b', 'c', [1, 2, 3] ]
  • Symbol.unscopables:拥有和继承属性名的一个对象的值被排除在与环境绑定的相关对象外。

  • Symbol.species:一个用于创建派生对象的构造器函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MyArray extends Array {
    // 覆盖 species 到父级的 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.toPrimitive:一个将对象转化为基本数据类型的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果
    var obj1 = {};
    console.log(+obj1); // NaN
    console.log(`${obj1}`); // "[object Object]"
    console.log(obj1 + ""); // "[object Object]"

    // 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果
    var obj2 = {
    [Symbol.toPrimitive](hint) {
    if (hint == "number") {
    return 10;
    }
    if (hint == "string") {
    return "hello";
    }
    return true;
    }
    };
    console.log(+obj2); // 10 -- hint 参数值是 "number"
    console.log(`${obj2}`); // "hello" -- hint 参数值是 "string"
    console.log(obj2 + ""); // "true" -- hint 参数值是 "default"
  • Symbol.toStringTag:用于对象的默认描述的字符串值。被 Object.prototype.toString() 使用。

    1
    2
    3
    4
    5
    6
    7
    class ValidatorClass {
    get [Symbol.toStringTag]() {
    return "Validator";
    }
    }

    Object.prototype.toString.call(new ValidatorClass()); // "[object Validator]"

方法

  • Symbol.for(key):使用给定的key搜索现有的symbol,如果找到则返回该symbol。否则将使用给定的key在全局symbol注册表中创建一个新的symbol。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
    Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol


    Symbol.for("bar") === Symbol.for("bar"); // true,证明了上面说的
    Symbol("bar") === Symbol("bar"); // false,Symbol() 函数每次都会返回新的一个 symbol


    var sym = Symbol.for("mario");
    sym.toString();
    // "Symbol(mario)",mario 既是该 symbol 在 symbol 注册表中的键名,又是该 symbol 自身的描述字符串
  • Symbol.keyFor(sym):从全局symbol注册表中,为给定的symbol检索一个共享的?symbol key。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 创建一个 symbol 并放入 Symbol 注册表,key 为 "foo"
    var globalSym = Symbol.for("foo");
    Symbol.keyFor(globalSym); // "foo"

    // 创建一个 symbol,但不放入 symbol 注册表中
    var localSym = Symbol();
    Symbol.keyFor(localSym); // undefined,所以是找不到 key 的

    // well-known symbol 们并不在 symbol 注册表中
    Symbol.keyFor(Symbol.iterator) // undefined

唯一性

1
2
3
4
5
6
7
let a1 = Symbol('a'),
a2 = Symbol('a');

a1 == a2; // false

let b1 = Symbol('b');
a1 == b1; // false

属性

Symbol属性值如下图:
p-symbol.jpg

更多可见:mdn Symbol

12 WeakSet/WeakMap

WeakSet与Set类似,也是不重复的值的集合。它与Set有两个区别:

  • 第一,WeakSet的成员只能是对象,而不能是其他类型的值。
  • 第二,WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用。即如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象是否还存在于WeakSet之中。

原因:垃圾回收机制依赖引用计数,如果一个值的引用次数不为0,垃圾回收机制就不会释放这块内存。结束使用改值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄露。而WeakSet里面的引用都不计入垃圾回收机制。

WeakSet适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在WeakSet里面的引用就会自动消失。由此可知,WeakSet的成员是不适合引用的,因为它会随时消失。另外,WeakSet内部有多少成员取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,也因为垃圾运行机制何时运行是不可预测的,因此ES6规定WeakSet不可遍历。

同样的特点也适用于WeakMap,在此不过多介绍。

应用场景:DOM、部署私有属性。更多可见:https://exploringjs.com/es6/ch_maps-sets.html#_use-cases-for-weakmaps