0
点赞
收藏
分享

微信扫一扫

【ES】152-重温基础:ES6系列(三)

【ES】152-重温基础:ES6系列(三)_数组

ES6系列目录

  • 1 let 和 const命令
  • 2 变量的解构赋值
  • 3 字符串的拓展
  • 4 正则的拓展
  • 5 数值的拓展
  • 6 函数的拓展
  • 7 数组的拓展
  • 对象的拓展
  • Symbol
  • 10 Set和Map数据结构
  • 11 Proxy
  • 12 Promise对象
  • 13 Iterator和 for...of循环
  • 14 Generator函数和应用
  • 15 Class语法和继承
  • 16 Module语法和加载实现

所有整理的文章都收录到我《Cute-JavaScript》系列文章中,访问地址:http://js.pingan8787.com


8 对象的拓展

8.1 属性的简洁表示

  1. let a = 'a1';
  2. let b = { a };  // b => { a : 'a1' }
  3. // 等同于
  4. let b = { a : a };

  5. function f(a, b){
  6.    return {a, b};
  7. }
  8. // 等同于
  9. function f (a, b){
  10.    return {a:a ,b:b};
  11. }

  12. let a = {
  13.    fun () {
  14.        return 'leo';
  15.    }
  16. }
  17. // 等同于
  18. let a = {
  19.    fun : function(){
  20.        return 'leo';
  21.    }
  22. }

8.2 属性名表达式

JavaScript​提供2种方法定义对象的属性

  1. // 方法1 标识符作为属性名
  2. a.f = true;

  3. // 方法2 字符串作为属性名
  4. a['f' + 'un'] = true;

延伸出来的还有:

  1. let a = 'hi leo';
  2. let b = {
  3.    [a]: true,
  4.    ['a'+'bc']: 123,
  5.    ['my' + 'fun'] (){
  6.        return 'hi';
  7.    }
  8. };
  9. // b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi'
  10. // b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; }

注意
属性名表达式不能与简洁表示法同时使用,否则报错。

  1. // 报错
  2. let a1 = 'aa';
  3. let a2 = 'bb';
  4. let b1 = {[a1]};

  5. // 正确
  6. let a1 = 'aa';
  7. let b1 = { [a1] : 'bb'};

8.3 Object.is()

Object.is()​ 用于比较两个值是否严格相等,在ES5时候只要使用相等运算符( ​==​)和严格相等运算符( ​===​)就可以做比较,但是它们都有缺点,前者会自动转换数据类型,后者的 ​NaN​​不等于自身,以及 ​+0​​等于 ​-0​。

  1. Object.is('a','a');   // true
  2. Object.is({}, {});    // false

  3. // ES5
  4. +0 === -0 ;           // true
  5. NaN === NaN;          // false

  6. // ES6
  7. Object.is(+0,-0);     // false
  8. Object.is(NaN,NaN);   // true

8.4 Object.assign()

Object.assign()​​方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。
基础用法
第一个参数是目标对象,后面参数都是源对象

  1. let a = {a:1};
  2. let b = {b:2};
  3. Object.assign(a,b);  // a=> {a:1,b:2}

注意

  • 若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。
  1. let a = {a:1, b:2};
  2. let b = {b:3, c:4};
  3. Object.assign(a, b); // a => {a:1, b:3, c:4}
  • 若只有一个参数,则返回该参数。
  1. let a = {a:1};
  2. Object.assign(a) === a;  // true
  • 若参数不是对象,则先转成对象后返回。
  1. typeof Object.assign(2); // 'object'
  • 由于 ​undefined​​或 ​​NaN​​无法转成对象,所以做为参数会报错。
  1. Object.assign(undefined) // 报错
  2. Object.assign(NaN);      // 报错
  • Object.assign()​实现的是浅拷贝。

Object.assign()​拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。

  1. let a = {a: {b:1}};
  2. let b = Object.assign({},a);
  3. a.a.b = 2;
  4. console.log(b.a.b);  // 2
  • 将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。
  1. Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3]


9 Symbol

9.1 介绍

ES6引入 ​Symbol​作为一种新的原始数据类型,表示独一无二的值,主要是为了防止属性名冲突
ES6之后,JavaScript一共有其中数据类型: ​​Symbol​​、 ​undefined​​、 ​null​​、 ​Boolean​​、 ​String​​、 ​Number​​、 ​Object​​。
简单实用:

  1. let a = Symbol();
  2. typeof a; // "symbol"

注意:

  • Symbol​​函数不能用 ​​new​​,会报错。由于 ​​Symbol​​是一个原始类型,不是对象,所以不能添加属性,它是类似于字符串的数据类型。
  • Symbol​都是不相等的,即使参数相同。
  1. // 没有参数
  2. let a1 = Symbol();
  3. let a2 = Symbol();
  4. a1 === a2; // false

  5. // 有参数
  6. let a1 = Symbol('abc');
  7. let a2 = Symbol('abc');
  8. a1 === a2; // false
  • Symbol​不能与其他类型的值计算,会报错。
  1. let a = Symbol('hello');
  2. a + " world!";  // 报错
  3. `${a} world!`;  // 报错

Symbol可以显式转换为字符串:

  1. let a1 = Symbol('hello');

  2. String(a1);    // "Symbol(hello)"
  3. a1.toString(); // "Symbol(hello)"

Symbol可以转换为布尔值,但不能转为数值:

  1. let a1 = Symbol();
  2. Boolean(a1);
  3. !a1;        // false

  4. Number(a1); // TypeError
  5. a1 + 1 ;    // TypeError

9.2 Symbol作为属性名

好处:防止同名属性,还有防止键被改写或覆盖。

  1. let a1 = Symbol();

  2. // 写法1
  3. let b = {};
  4. b[a1] = 'hello';

  5. // 写法2
  6. let b = {
  7.    [a1] : 'hello'
  8. }

  9. // 写法3
  10. let b = {};
  11. Object.defineProperty(b, a1, {value : 'hello' });

  12. // 3种写法 结果相同
  13. b[a1]; // 'hello'

需要注意: Symbol作为对象属性名时,不能用点运算符,并且必须放在方括号内。

  1. let a = Symbol();
  2. let b = {};

  3. // 不能用点运算
  4. b.a = 'hello';
  5. b[a] ; // undefined
  6. b['a'] ; // 'hello'

  7. // 必须放在方括号内
  8. let c = {
  9.    [a] : function (text){
  10.        console.log(text);
  11.    }
  12. }
  13. c[a]('leo'); // 'leo'

  14. // 上面等价于 更简洁
  15. let c = {
  16.    [a](text){
  17.        console.log(text);
  18.    }
  19. }

常常还用于创建一组常量,保证所有值不相等:

  1. let a = {};
  2. a.a1 = {
  3.    AAA: Symbol('aaa'),
  4.    BBB: Symbol('bbb'),
  5.    CCC: Symbol('ccc')
  6. }

9.3 应用:消除魔术字符串

魔术字符串:指代码中多次出现,强耦合的字符串或数值,应该避免,而使用含义清晰的变量代替。

  1. function f(a){
  2.    if(a == 'leo') {
  3.        console.log('hello');
  4.    }
  5. }
  6. f('leo');   // 'leo' 为魔术字符串

常使用变量,消除魔术字符串:

  1. let obj = {
  2.    name: 'leo'
  3. };
  4. function f (a){
  5.    if(a == obj.name){
  6.        console.log('hello');
  7.    }
  8. }
  9. f(obj.name); // 'leo'

使用Symbol消除强耦合,使得不需关系具体的值:

  1. let obj = {
  2.    name: Symbol()
  3. };
  4. function f (a){
  5.    if(a == obj.name){
  6.        console.log('hello');
  7.    }
  8. }
  9. f(obj.name);

9.4 属性名遍历

Symbol作为属性名遍历,不出现在 ​for...in​​、 ​for...of​​循环,也不被 ​Object.keys()​​、 ​Object.getOwnPropertyNames()​​、 ​JSON.stringify()​返回。

  1. let a = Symbol('aa'),b= Symbol('bb');
  2. let obj = {
  3.    [a]:'11', [b]:'22'
  4. }
  5. for(let k of Object.values(obj)){console.log(k)}
  6. // 无输出

  7. let obj = {};
  8. let aa = Symbol('leo');
  9. Object.defineProperty(obj, aa, {value: 'hi'});

  10. for(let k in obj){
  11.    console.log(k); // 无输出
  12. }

  13. Object.getOwnPropertyNames(obj);   // []
  14. Object.getOwnPropertySymbols(obj); // [Symbol(leo)]

Object.getOwnPropertySymbols​方法返回一个数组,包含当前对象所有用做属性名的Symbol值。

  1. let a = {};
  2. let a1 = Symbol('a');
  3. let a2 = Symbol('b');
  4. a[a1] = 'hi';
  5. a[a2] = 'oi';

  6. let obj = Object.getOwnPropertySymbols(a);
  7. obj; //  [Symbol(a), Symbol(b)]

另外可以使用 ​Reflect.ownKeys​方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

  1. let a = {
  2.    [Symbol('leo')]: 1,
  3.    aa : 2,
  4.    bb : 3,
  5. }
  6. Reflect.ownKeys(a); // ['aa', 'bb',Symbol('leo')]

由于Symbol值作为名称的属性不被常规方法遍历获取,因此常用于定义对象的一些非私有,且内部使用的方法。

9.5 Symbol.for()、Symbol.keyFor()

  • Symbol.for()
    用于重复使用一个Symbol值,接收一个字符串作为参数,若存在用此参数作为名称的Symbol值,返回这个Symbol,否则新建并返回以这个参数为名称的Symbol值。
  1. let a = Symbol.for('aaa');
  2. let b = Symbol.for('aaa');

  3. a === b;  // true

Symbol()​​ 和 ​Symbol.for()​区别:

  1. Symbol.for('aa') === Symbol.for('aa'); // true
  2. Symbol('aa') === Symbol('aa');         // false
  • Symbol.keyFor()
    用于返回一个已使用的Symbol类型的key:
  1. let a = Symbol.for('aa');
  2. Symbol.keyFor(a);   //  'aa'

  3. let b = Symbol('aa');
  4. Symbol.keyFor(b);   //  undefined

9.6 内置的Symbol值

ES6提供11个内置的Symbol值,指向语言内部使用的方法:

  • 1.Symbol.hasInstance
    当其他对象使用 ​​instanceof​​运算符,判断是否为该对象的实例时,会调用这个方法。比如, ​​fooinstanceofFoo​​在语言内部,实际调用的是 ​​Foo[Symbol.hasInstance](foo)​​。
  1. class P {
  2.    [Symbol.hasInstance](a){
  3.        return a instanceof Array;
  4.    }
  5. }
  6. [1, 2, 3] instanceof new P(); // true

P是一个类,new P()会返回一个实例,该实例的 ​Symbol.hasInstance​​方法,会在进行 ​instanceof​​运算时自动调用,判断左侧的运算子是否为 ​Array​的实例。

  • 2.Symbol.isConcatSpreadable
    值为布尔值,表示该对象用于 ​​Array.prototype.concat()​时,是否可以展开。
  1. let a = ['aa','bb'];
  2. ['cc','dd'].concat(a, 'ee');
  3. // ['cc', 'dd', 'aa', 'bb', 'ee']
  4. a[Symbol.isConcatSpreadable]; // undefined

  5. let b = ['aa','bb'];
  6. b[Symbol.isConcatSpreadable] = false;
  7. ['cc','dd'].concat(b, 'ee');
  8. // ['cc', 'dd',[ 'aa', 'bb'], 'ee']
  • 3.Symbol.species
    指向一个构造函数,在创建衍生对象时会使用,使用时需要用 ​​get​取值器。
  1. class P extends Array {
  2.    static get [Symbol.species](){
  3.        return this;
  4.    }
  5. }

解决下面问题:

  1. // 问题:  b应该是 Array 的实例,实际上是 P 的实例
  2. class P extends Array{}

  3. let a = new P(1,2,3);
  4. let b = a.map(x => x);

  5. b instanceof Array; // true
  6. b instanceof P; // true

  7. // 解决:  通过使用 Symbol.species
  8. class P extends Array {
  9.  static get [Symbol.species]() { return Array; }
  10. }
  11. let a = new P();
  12. let b = a.map(x => x);
  13. b instanceof P;     // false
  14. b instanceof Array; // true
  • 4.Symbol.match
    当执行 ​​str.match(myObject)​,传入的属性存在时会调用,并返回该方法的返回值。
  1. class P {
  2.    [Symbol.match](string){
  3.        return 'hello world'.indexOf(string);
  4.    }
  5. }
  6. 'h'.match(new P());   // 0
  • 5.Symbol.replace 当该对象被 ​String.prototype.replace​方法调用时,会返回该方法的返回值。
  1. let a = {};
  2. a[Symbol.replace] = (...s) => console.log(s);
  3. 'Hello'.replace(a , 'World') // ["Hello", "World"]
  • 6.Symbol.hasInstance
    当该对象被 ​​String.prototype.search​方法调用时,会返回该方法的返回值。
  1. class P {
  2.    constructor(val) {
  3.        this.val = val;
  4.    }
  5.    [Symbol.search](s){
  6.        return s.indexOf(this.val);
  7.    }
  8. }
  9. 'hileo'.search(new P('leo')); // 2
  • 7.Symbol.split
    当该对象被 ​​String.prototype.split​方法调用时,会返回该方法的返回值。
  1. // 重新定义了字符串对象的split方法的行为
  2. class P {
  3.    constructor(val) {
  4.        this.val = val;
  5.    }
  6.    [Symbol.split](s) {
  7.        let i = s.indexOf(this.val);
  8.        if(i == -1) return s;
  9.        return [
  10.            s.substr(0, i),
  11.            s.substr(i + this.val.length)
  12.        ]
  13.    }
  14. }

  15. 'helloworld'.split(new P('hello')); // ["hello", ""]
  16. 'helloworld'.split(new P('world')); // ["", "world"]
  17. 'helloworld'.split(new P('leo'));   // "helloworld"
  • 8.Symbol.iterator
    对象进行 ​​for...of​​循环时,会调用 ​​Symbol.iterator​​方法,返回该对象的默认遍历器。
  1. class P {
  2.    *[Symbol.interator]() {
  3.        let i = 0;
  4.        while(this[i] !== undefined ) {
  5.            yield this[i];
  6.            ++i;
  7.        }
  8.    }
  9. }
  10. let a = new P();
  11. a[0] = 1;
  12. a[1] = 2;

  13. for (let k of a){
  14.    console.log(k);
  15. }
  • 9.Symbol.toPrimitive
    该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。调用时,需要接收一个字符串参数,表示当前运算模式,运算模式有:
  • Number : 此时需要转换成数值
  • String : 此时需要转换成字符串
  • Default : 此时可以转换成数值或字符串
  1. let obj = {
  2.  [Symbol.toPrimitive](hint) {
  3.    switch (hint) {
  4.      case 'number':
  5.        return 123;
  6.      case 'string':
  7.        return 'str';
  8.      case 'default':
  9.        return 'default';
  10.      default:
  11.        throw new Error();
  12.     }
  13.   }
  14. };

  15. 2 * obj // 246
  16. 3 + obj // '3default'
  17. obj == 'default' // true
  18. String(obj) // 'str'
  • 10.Symbol.toStringTag
    在该对象上面调用 ​​Object.prototype.toString​​方法时,如果这个属性存在,它的返回值会出现在 ​​toString​​方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制 ​​[objectObject​​]或 ​​[objectArray]​​中 ​​object​​后面的那个字符串。
  1. // 例一
  2. ({[Symbol.toStringTag]: 'Foo'}.toString())
  3. // "[object Foo]"

  4. // 例二
  5. class Collection {
  6.  get [Symbol.toStringTag]() {
  7.    return 'xxx';
  8.  }
  9. }
  10. let x = new Collection();
  11. Object.prototype.toString.call(x) // "[object xxx]"
  • 11.Symbol.unscopables
    该对象指定了使用with关键字时,哪些属性会被with环境排除。
  1. // 没有 unscopables 时
  2. class MyClass {
  3.  foo() { return 1; }
  4. }

  5. var foo = function () { return 2; };

  6. with (MyClass.prototype) {
  7.  foo(); // 1
  8. }

  9. // 有 unscopables 时
  10. class MyClass {
  11.  foo() { return 1; }
  12.  get [Symbol.unscopables]() {
  13.    return { foo: true };
  14.  }
  15. }

  16. var foo = function () { return 2; };

  17. with (MyClass.prototype) {
  18.  foo(); // 2
  19. }

上面代码通过指定 ​Symbol.unscopables​​属性,使得 ​with​​语法块不会在当前作用域寻找 ​foo​​属性,即 ​foo​将指向外层作用域的变量。


【ES】152-重温基础:ES6系列(三)_字符串_02


举报

相关推荐

0 条评论