• 06【对象的扩展】


    对象的扩展

    1.属性的简洁表示法

    ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

    const foo = 'bar';
    const baz = {foo};
    baz // {foo: "bar"}
    
    // 等同于
    const baz = {foo: foo};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上面代码中,变量foo直接写在大括号里面。这时,属性名就是变量名, 属性值就是变量值。下面是另一个例子。

    function f(x, y) {
      return {x, y};
    }
    
    // 等同于
    
    function f(x, y) {
      return {x: x, y: y};
    }
    
    f(1, 2) // Object {x: 1, y: 2}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    除了属性简写,方法也可以简写。

    const o = {
      method() {
        return "Hello!";
      }
    };
    
    // 等同于
    
    const o = {
      method: function() {
        return "Hello!";
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    下面是一个实际的例子。

    let birth = '2000/01/01';
    
    const Person = {
    
      name: '张三',
    
      //等同于birth: birth
      birth,
    
      // 等同于hello: function ()...
      hello() { console.log('我的名字是', this.name); }
    
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这种写法用于函数的返回值,将会非常方便。

    function getPoint() {
      const x = 1;
      const y = 10;
      return {x, y};
    }
    
    getPoint()
    // {x:1, y:10}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.方括号语法

    2.1 方括号语法的用法

    const prop = 'age';
    const person = {};
    person.prop = 18;
    console.log(person);	// { prop: 18 }
    
    // -----------------------------------------
    
    const prop = 'age';
    const person = {};
    person[prop] = 18;
    console.log(person);	// { age: 18 }
    
    // -----------------------------------------
    
    // ES6 增强
    const prop = 'age';
    const person = {
        [prop]: 18
    };
    console.log(person);	// { age: 18 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2 方括号中可以放什么

    // [值、可以得到值的表达式]
    const prop = 'age';
    const func = () => 'age2';
    const person = {
        [prop]: 18,
        [func()]: 24,
        ['sex']: 'man',
        ['s' + 'ex2']: 'womam'
    };
    console.log(person);	// { age: 18, age2: 24, sex: 'man', sex2: 'womam' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意,属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object],这一点要特别小心。

    const keyA = {a: 1};
    const keyB = {b: 2};
    
    const myObject = {
      [keyA]: 'valueA',
      [keyB]: 'valueB'
    };
    
    myObject // Object {[object Object]: "valueB"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上面代码中,[keyA][keyB]得到的都是[object Object],所以[keyB]会把[keyA]覆盖掉,而myObject最后只有一个[object Object]属性。

    2.3 方括号语法和点语法的区别

    1. 点语法是方括号语法的特殊形式
    2. 属性名由数字、字母、下划线以及 $ 构成,并且数字还不能打头的时候可以使用点语法(合法标识符)
    3. 能用点语法优先使用点语法
    const person = {
        age: 18
    };
    
    person.age 等价于 person['age']
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.super 关键字

    我们知道,this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象

    const proto = {
      foo: 'hello'
    };
    
    const obj = {
      foo: 'world',
      find() {
        return super.foo;
      }
    };
    
    Object.setPrototypeOf(obj, proto);
    obj.find() // "hello"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    上面代码中,对象obj.find()方法之中,通过super.foo引用了原型对象protofoo属性。

    注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。

    // 报错
    const obj = {
      foo: super.foo
    }
    
    // 报错
    const obj = {
      foo: () => super.foo
    }
    
    // 报错
    const obj = {
      foo: function () {
        return super.foo
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    上面三种super的用法都会报错,因为对于 JavaScript 引擎来说,这里的super都没有用在对象的方法之中。第一种写法是super用在属性里面,第二种和第三种写法是super用在一个函数里面,然后赋值给foo属性。目前,只有对象方法的简写法可以让 JavaScript 引擎确认,定义的是对象的方法。

    JavaScript 引擎内部,super.foo等同于Object.getPrototypeOf(this).foo(属性)或Object.getPrototypeOf(this).foo.call(this)(方法)。

    const proto = {
      x: 'hello',
      foo() {
        console.log(this.x);
      },
    };
    
    const obj = {
      x: 'world',
      foo() {
        super.foo();
      }
    }
    
    Object.setPrototypeOf(obj, proto);
    
    obj.foo() // "world"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    上面代码中,super.foo指向原型对象protofoo方法,但是绑定的this却还是当前对象obj,因此输出的就是world

    4.对象的展开运算符

    4.1 展开对象

    对象不能直接展开,必须在 {} 中展开。

    const apple = {
        color: '红色',
        shape: '球形',
        taste: '甜'
    };
    console.log({...apple});			// { color: '红色', shape: '球形', taste: '甜' }
    console.log({...apple} === apple);	// false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4.2 合并对象

    const apple = {
        color: '红色',
        shape: '球形',
        taste: '甜'
    };
    
    const pen = {
        color: '黑色',
        shape: '圆柱形',
        use: '写字'
    };
    
    // 新对象拥有全部属性,相同属性,后者覆盖前者
    console.log({...apple, ...pen});	// { color: '黑色', shape: '圆柱形', taste: '甜', use: '写字' }
    console.log({...pen, ...apple});	// { color: '红色', shape: '球形', use: '写字', taste: '甜' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.3 注意事项

    4.3.1 空对象的展开

    如果展开一个空对象,则没有任何效果。

    console.log({...{}});			// {}
    console.log({...{}, a: 1});		// { a: 1 }
    
    • 1
    • 2

    4.3.2 非对象的展开

    如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来(没有属性便为空)。

    console.log({...1});			// {}
    console.log(new Object(1));		// [Number: 1]
    console.log({...undefined});	// {}
    console.log({...null});			// {}
    console.log({...true});			// {}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.3.3 字符串的展开

    如果展开运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象。

    // 字符串在对象中展开
    console.log({...'alex'});		// { '0': 'a', '1': 'l', '2': 'e', '3': 'x' }
    
    // 字符串在数组中展开
    console.log([...'alex']);		// [ 'a', 'l', 'e', 'x' ]
    
    // 字符串直接展开
    console.log(...'alex');			// a l e x
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.3.4 数组的展开

    console.log({...[1, 2, 3]});	// { '0': 1, '1': 2, '2': 3 }
    
    • 1

    4.3.5 对象中对象属性的展开

    不会展开对象中的对象属性。

    const apple = {
        feature: {
            taste: '甜'
        }
    };
    
    const pen = {
        feature: {
            color: '黑色',
            shape: '圆柱形'
        },
        use: '写字'
    };
    
    console.log({...apple});			// { feature: { taste: '甜' } }
    
    // feature 会直接覆盖,因为 feature 不能展开
    console.log({...apple, ...pen});	// { feature: { color: '黑色', shape: '圆柱形' }, use: '写字' }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4.4 对象展开运算符的应用

    4.4.1 复制对象

    const a = {x: 1, y: 2};
    const c = {...a};
    console.log(c, c === a);
    // { x: 1, y: 2 } false
    
    • 1
    • 2
    • 3
    • 4

    4.4.2 用户参数和默认参数

    const logUser = userParam => {
        const defaultPeram = {
            username: 'ZhangSan',
            age: 0,
            sex: 'male'
        };
    
        const param = {...defaultPeram, ...userParam};
        console.log(param.username, param.age, param.sex);
    };
    
    logUser({username: 'jerry'});	// jerry 0 male
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    再优化:

    const logUser = userParam => {
        const defaultPeram = {
            username: 'ZhangSan',
            age: 0,
            sex: 'male'
        };
    
        const {username, age, sex} = {...defaultPeram, ...userParam};
        console.log(username, age, sex);
    };
    
    logUser({username: 'jerry'});	// jerry 0 male
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    5.对象的新增方法

    5.1 Object.is()

    ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。

    ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

    Object.is('foo', 'foo')
    // true
    Object.is({}, {})
    // false
    
    • 1
    • 2
    • 3
    • 4

    不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

    +0 === -0 //true
    NaN === NaN // false
    
    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5.2 Object.assign()

    Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

    const target = { a: 1 };
    
    const source1 = { b: 2 };
    const source2 = { c: 3 };
    
    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

    const target = { a: 1, b: 1 };
    
    const source1 = { b: 2, c: 2 };
    const source2 = { c: 3 };
    
    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    汇总

    // 基本用法
    // Object.assign(目标对象, 源对象1, 源对象2, ...);
    const apple = {
        color: '红色',
        shape: '圆形',
        taste: '甜'
    };
    const pen = {
        color: '黑色',
        shape: '圆柱形',
        use: '写字'
    };
    console.log(Object.assign(apple, pen));	
    // 后面的覆盖前面的(最终返回的不是新的,而是修改了前面的)
    // { color: '黑色', shape: '圆柱形', taste: '甜', use: '写字' }
    // Object.assign 直接合并到了第一个参数中,返回的就是合并后的对象
    console.log(apple);	// { color: '黑色', shape: '圆柱形', taste: '甜', use: '写字' }
    console.log(Object.assign(apple, pen) === apple);	// true
    
    
    // 可以合并多个对象
    // 第一个参数使用一个空对象来实现合并返回一个新对象的目的
    console.log(Object.assign({}, apple, pen));	// { color: '黑色', shape: '圆柱形', taste: '甜', use: '写字' }
    console.log(apple);	// { color: '红色', shape: '圆形', taste: '甜' }
    console.log({...apple, ...pen}); // { color: '黑色', shape: '圆柱形', taste: '甜', use: '写字' }
    
    
    // 注意事项
    // (1) 基本数据类型作为源对象
    // 与对象的展开类似,先转换成对象,再合并
    console.log(Object.assign({}, undefined));	// {}
    console.log(Object.assign({}, null));		// {}
    console.log(Object.assign({}, 1));			// {}
    console.log(Object.assign({}, true));		// {}
    console.log(Object.assign({}, 'str'));		// { '0': 's', '1': 't', '2': 'r' }
    // (2) 同名属性的替换
    // 后面的直接覆盖前面的
    const apple = {
        color: ['红色', '黄色'],
        shape: '圆形',
        taste: '甜'
    };
    const pen = {
        color: ['黑色', '银色'],
        shape: '圆柱形',
        use: '写字'
    };
    console.log(Object.assign({}, apple, pen));	// { color: [ '黑色', '银色' ], shape: '圆柱形', taste: '甜', use: '写字' }
    
    
    // 应用
    // 合并默认参数和用户参数
    const logUser = userOptions => {
        const DEFAULTS = {
            username: 'ZhangSan',
            age: 0,
            sex: 'male'
        };
    
        const options = Object.assign({}, DEFAULTS, userOptions);
        console.log(options);
    };
    logUser();						// { username: 'ZhangSan', age: 0, sex: 'male' }
    logUser({});					// { username: 'ZhangSan', age: 0, sex: 'male' }
    logUser({username: 'Alex'});	// { username: 'Alex', age: 0, sex: 'male' }
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    5.3 Object.keys()、Object.values() 和 Object.entries()

    Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。

    Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

    Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

    // 基本用法
    const person = {
        name: 'Alex',
        age: 18
    };
    // 返回键数组
    console.log(Object.keys(person));		// [ 'name', 'age' ]
    // 返回值数组
    console.log(Object.values(person));		// [ 'Alex', 18 ]
    // 返回键值二维数组
    console.log(Object.entries(person));	// [ [ 'name', 'Alex' ], [ 'age', 18 ] ]
    
    
    // 与数组类似方法的区别
    console.log([1, 2].keys());			// Object [Array Iterator] {}
    console.log([1, 2].values());		// Object [Array Iterator] {}
    console.log([1, 2].entries());		// Object [Array Iterator] {}
    // 数组的 keys()、values()、entries() 等方法是实例方法,返回的都是 Iterator
    // 对象的 Object.keys()、Object.values()、Object.entries() 等方法是构造函数方法,返回的是数组
    
    
    // 应用(使用 for...of 循环遍历对象)
    const person = {
        name: 'Alex',
        age: 18
    };
    for (const key of Object.keys(person)) {
        console.log(key);		
    }
    // name
    // age
    for (const value of Object.values(person)) {
        console.log(value);		
    }
    // Alex
    // 18
    for (const entries of Object.entries(person)) {
        console.log(entries);	
    }
    // [ 'name', 'Alex' ]
    // [ 'age', 18 ]
    for (const [key, value] of Object.entries(person)) {
        console.log(key, value);
    }
    // name Alex
    // age 18
    
    // Object.keys()/values()/entires() 并不能保证顺序一定是你看到的样子,这一点和 for in 是一样的
    // 如果对遍历顺序有要求那么不能用 for in 以及这种方法,而要用其他方法
    
    • 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
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
  • 相关阅读:
    如何防止网络安全攻击
    macOS磁盘分区调整软件--Paragon Camptune X 中文
    docker使用php较老版本出现pecl无法安装扩展问题
    Sqoop
    Codeforces Round 889 (Div. 2)A~C1题解
    I2C协议
    1003 Emergency
    一文读懂TCP的三次握手(详细图解)
    maven打包命令打出的可执行的jar包和可依赖的jar包的区别
    第5章 课后习题实训---二维数组
  • 原文地址:https://blog.csdn.net/DSelegent/article/details/126558566