对象的扩展

属性的简洁表示法

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

const foo = 'bar';
const baz = {
    foo
};
console.log(baz); //{ foo: 'bar' }

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

function f(x, y) {
    return {
        x,
        y
    };
}

// 等同于
function f(x, y) {
    return {
        x: x,
        y: y
    };
}
f(1, 2); // {x: 1, y: 2}

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

const o = {
    method() {
        return "Hello!";
    }
};
// 等同于
const o = {
    method: function () {
        return "Hello!";
    }
};

下面是一个实际的例子。

let birth = '2000/01/01';

const Person = {

    name: '张三',

    //等同于birth: birth
    birth,

    // 等同于hello: function ()...
    hello() {
        console.log('我的名字是', this.name);
    }

};
Person.hello(); // 我的名字是 张三

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

function getPoint() {
    const x = 1;
    const y = 10;
    return {
        x,
        y
    };
}

var result = getPoint()
console.log(result); // {x:1, y:10}

CommonJS 模块输出一组变量,就非常合适使用简洁写法。

let ms = {};

function getItem(key) {
    return key in ms ? ms[key] : null;
}

function setItem(key, value) {
    ms[key] = value;
}

function clear() {
    ms = {};
}

module.exports = {
    getItem,
    setItem,
    clear
};
// 等同于
module.exports = {
    getItem: getItem,
    setItem: setItem,
    clear: clear
};

属性名表达式

JavaScript 定义对象的属性,有两种方法。

// 方法一
obj.foo = true;

// 方法二
obj['a' + 'bc'] = 123;

ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。

let propKey = 'foo';

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};

下面是另一个例子

let lastWord = 'last word';

const a = {
    'first word': 'hello',
    [lastWord]: 'world'
};

console.log(a['first word']); // "hello"
console.log(a[lastWord]); // "world"
console.log(a['last word']); // "world"

表达式还可以用于定义方法名。

let obj = {
  ['h' + 'ello']() {
    return 'hi';
  }
};

obj.hello() // hi

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

const keyA = {
    a: 1
};
const keyB = {
    b: 2
};

const myObject = {
    [keyA]: 'valueA',
    [keyB]: 'valueB'
};

console.log(myObject); //{ '[object Object]': 'valueB' }

super 关键字

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

 

const proto = {
    foo: 'hello'
};

const obj = {
    foo: 'world',
    find() {
        return super.foo; // 
    }
};

Object.setPrototypeOf(obj, proto); // 设置obj实例的原型对象为proto
console.log(obj.find()); // "hello"

 

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

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

const proto = {
  x: 'hello',
  foo() {
    console.log(this.x);
  },
};

const obj = {
  x: 'world',
  foo() {
    super.foo();
  }
}

Object.setPrototypeOf(obj, proto);

obj.foo() // "world"

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

对象的扩展运算符

对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。

 

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // {a: 3, b: 4}

上面代码中,变量z是解构赋值所在的对象。它获取等号右边的所有尚未读取的键(ab),将它们连同值一起拷贝过来。

注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。

 

let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2

 

上面代码中,x是解构赋值所在的对象,拷贝了对象obja属性。a属性引用了一个对象,修改这个对象的值,会影响到解构赋值对它的引用。

扩展运算符的解构赋值,不能复制继承自原型对象的属性。

let o1 = {a:1};
let o2 = {b:2};
o2.__proto__ = o1;
let {...o3} = o2;
console.log(o3.b);  // 2
console.log(o3.a);  // undefined

下面是另一个例子。

const o = Object.create({ x: 1, y: 2 }); // 原型对象上的属性
console.log(o); // {}
o.z = 3; // 自身的属性

let { x, ...newObj } = o;
let { y, z } = newObj;
x // 1
y // undefined
z // 3

// Object.create创建一个新的对象{}返回被o接受,将现有对象{ x: 1, y: 2 }作为新对象的__proto__.
// 上面代码中,变量x是单纯的解构赋值,所以可以读取对象o继承的属性;变量y和z是扩展运算符的解构赋值,只能读取对象o自身的属性,所以变量z可以赋值成功,变量y取不到值。

扩展运算符 

对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

由于数组是特殊的对象,所以对象的扩展运算符也可以用于数组。

let foo = { ...['a', 'b', 'c'] };
foo
// {0: "a", 1: "b", 2: "c"}

 

posted @ 2021-07-26 15:57  半白半黑  阅读(32)  评论(0)    收藏  举报