ES6 对象的扩展

Posted Mr.隐士

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6 对象的扩展相关的知识,希望对你有一定的参考价值。

一、 对象成员 的扩展

1. 对象成员 的简洁写法

  • 属性
const foo = ‘bar‘;
const result = {foo};

// 上式 等同于:
const foo = ‘bar‘;
const result = {foo: foo};
  • 方法
const o = {
    method() {
        return "Hello!";
    }
};

// 上式 等同于

const o = {
    method: function() {
        return "Hello!";
    }
};

2. 自定义创建对象时,属性名 可以是 动态表达式

  • 举例说明:
// 如下:ES6支持;ES5不支持


let lastWord = ‘last word‘;

const a = {
    ‘first word‘: ‘hello‘,
    [lastWord]: ‘world‘
};
  • 注意区分:
    • ES5、ES6 都支持给 已创建好的对象 添加属性时,属性名为动态表达式
    • ES5 不支持在 自定义创建对象时, 为对象添加 动态表达式的属性
    • ES6 支持在自 自定义创建对象时, 为对象动态添加属性
// 如下:ES5 、ES6 都支持
let obj = {};
const aa = ‘name‘;

obj[aa] = ‘zhang‘;
  • 约束: 属性名表达式与简洁表示法,不能同时使用,会报错
// 报错
const foo = ‘bar‘;
const bar = ‘abc‘;
const baz = { [foo] };

// 正确
const foo = ‘bar‘;
const baz = { [foo]: ‘abc‘};

二、 对象的 扩展运算符

1. 作用:取出对象中 自身 可枚举的、非继承 的属性

let z = { a: 3, b: 4 };
let result = { ...z };
console.log(result);    // { a: 3, b: 4 }

2. 扩展运算符 结合 解构赋值

  • 结构赋值时,尚未被读取的属性,都会被分配到指定的对象上面
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 }
  • 解构赋值不是最后一个参数,所以会报错
let { ...x, y, z } = obj; // 句法错误
let { x, ...y, ...z } = obj; // 句法错误
  • 解构赋值是 浅拷贝;不复制 原型上的属性
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3 // { b: 2 }

3. 等同于 Object.assign( ) :对象属性拷贝合并(浅拷贝)

  • 重名属性,后者覆盖前者
let result = { ...a, ...b };

// 等同于
let result = Object.assign({}, a, b);
  • 不能用于对象的 深拷贝: 对象的属性如果是对象,则为属性对象的 浅拷贝:只是拷贝了 属性对象的指针
const a = {
    name: ‘zhang‘,
    info: {
        age: 18
    }
};
const aClone = {...a};

a.name = ‘xin‘;
a.info.age = 20;
console.log(aClone);    // aClone.name 值为 ‘zhang‘;aClone.info.age 值为20

三、 对象的合并:Object.assign( )

1. 语法 Object.assign(target, source1, source2)

  • 参数target 目标对象

  • 参数source1source2 源对象

  • 返回值: 合并了 源对象属性的 目标对象指针

2. 作用:对象属性的合并

  • 将源对象 所有可枚举的、非继承的属性, 合并到 目标对象中

  • 如果 目标对象 与 源对象 有同名属性、源对象 与 源对象 有重名属性:后者覆盖前者

3. 对象的属性如果是对象,则为属性对象的 浅拷贝:只是拷贝了 属性对象的指针

  • 如果 源对象 某个属性的值是对象,那么目标对象拷贝,得到的是这个对象的引用
const a = {age: 20};
const b = {
    info: {
        name: ‘zhang‘,
        sex: ‘man‘
    }
};

const result = Object.assign(a, b);

console.log(result.info === b.info);    // true
  • 注意: Object.assign( ) 不能用于对象的深拷贝,举例如下
const a = {
    name: ‘zhang‘,
    info: {
        age: 18
    }
};
const aClone = Object.assign({}, a);

console.log(a === aClone);      // 输出false;但改变了 a.info 中的属性;aClone.info 中的属性也会变化

4. 如果参数不是对象,会被默认转成对象(了解)

  • 如果参数不是对象,会默认将参数转为对象(但 null / undefined 不能转为对象)

  • 参数target:(目标对象)
    • 若 值为 null / undefined, 会直接报错
    • 若值为 string 类型:会将字符串转为包装对象(数组形式); ‘ab‘ 变为 [0: ‘a‘, 1: ‘b‘]
    • 若值为 Number 类型:会将 数值 转为包装对象
    • 若值为 Boolean 类型:会将 布尔值 转为包装对象
  • 参数source1:(源对象)
    • 若 值为 null / undefined,会跳过,不报错
    • 若值为 string 类型:会将字符串转为包装对象(数组形式); ‘ab‘ 变为 [0: ‘a‘, 1: ‘b‘]
    • 若值为 Number 类型:会被忽略
    • 若值为 Boolean 类型: 会被忽略

四、对象的遍历:Object.keys( )、Object.values( )、Object.entries( )

1. ES5 引入 Object.keys( )

  • 作用: 遍历 自身 可枚举的、非继承的属性键值

  • 语法:
    • Object.keys(obj)

    • 参数: 要遍历的对象

    • 返回值: 对象 键名 构成的 一维数组(键名)

var obj = { foo: ‘bar‘, baz: 42 };

console.log(Object.keys(obj));      // ["foo", "baz"]

2. ES6 补充 Object.values( )

  • 作用: 遍历 自身 可枚举的、非继承的属性键值

  • 语法:
    • Object.values(obj)

    • 参数: 要遍历的对象

    • 返回值: 对象 键值 构成的 一维数组(键值)

var obj = { foo: ‘bar‘, baz: 42 };

console.log(Object.values(obj));      // ["bar", 42]

3. ES6 补充 Object.entries( )

  • 语法:
    • Object.values(obj)

    • 参数: 要遍历的对象

    • 返回值: 对象 键值 构成的 二维数组(键名 、键值)

const obj = { foo: ‘bar‘, baz: 42 };

console.log(Object.entries(obj));   // [ ["foo", "bar"], ["baz", 42] ]
  • 作用: 遍历 自身 可枚举的、非继承的属性键名、键值
    • 遍历对象

    • 将对象转为真正的Map结构

const obj = { foo: ‘bar‘, baz: 42 };
const map = new Map(Object.entries(obj));
map // Map { foo: "bar", baz: 42 }

4. 遍历 对象属性 的方法汇总

  • (1)遍历对象属性 的几种方法:
    • for...in:遍历 对象属性 (含:可枚举的、继承的;不含:Symbol 类型的)

    • Object.keys(obj)Object.values(obj)Object.entries(obj):返回一个数组;(含:可枚举的;不含:继承的、Symbol类型的)

    • Object.getOwnPropertyNames(obj):返回一个键名数组;(含:可枚举的、不可枚举的;不含:继承的、Symbol类型的)

    • Object.getOwnPropertySymbols(obj):返回一个 Symbol 键值的数组;(只含:Symbol 类型的)

    • Reflect.ownKeys(obj):返回一个数组, (含:除继承外的 所有属性,无论 是否可比遍历、无论Symbol类型;不含:继承的)

  • (2)遍历对象属性的顺序: 以上五种方法都遵循,如下
    • 首先遍历所有数值键,按照数值升序排列
    • 其次遍历所有字符串键,按照加入时间升序排列
    • 最后遍历所有 Symbol 键,按照加入时间升序排列

五、对象属性的 底层描述

1. 回顾:获取对象属性 的描述对象 Object.getOwnPropertyDescriptor(obj, property)

  • 对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为

  • Object.getOwnPropertyDescriptor(obj, property) 方法来 获取对象属性 的描述对象

const obj = { foo: 123 };
const result = Object.getOwnPropertyDescriptor(obj, ‘foo‘);

console.log(result);
//  {
//    value: 123,           // 属性值
//    writable: true,       // 可写性:是否可修改
//    enumerable: true,     // 可枚举性:是否可枚举
//    configurable: true    // 可配置性:是否可以用 delete删除
//  }

2. 回顾:给对象添加属性 和 属性的描述信息 Object.defineProperty(obj, property, descriptor)

  • 语法:
    • 参数1:要添加属性的对象

    • 参数2:要添加属性的键值

    • 参数3:要添加属性的描述信息

var obj = {};

Object.defineProperty(obj, "a", {
    value : 1,
    writable : true,
    configurable : true,
    enumerable : true
});

// 如上代码,等同于:
var o = {};
o.a = 1;

3. 回顾:判断对象的属性 是否是继承的 / 自身提供的 myObject.hasOwnProperty(property)

  • 语法:
    • 参数:要判断的属性

    • 返回值:布尔值(true:对象自身属性;false:对象继承属性)

4. 回顾:对象属性的 可枚举性 Object.getOwnPropertyDescriptor(obj, property).enumerable

  • 查看对象的属性是否 可枚举
console.log(Object.getOwnPropertyDescriptor([], ‘length‘).enumerable);

// false
  • 4个操作:会忽略 enumerablefalse 的属性
    • for...in遍历 对象自身、可枚举、继承 的属性(含继承、不含 Symbol 属性)

    • 扩展运算符 ...遍历 对象自身、可枚举属性(不含继承)

    • Object.keys()遍历 对象自身、可枚举属性(不含继承、 不含 Symbol 属性)

    • JSON.stringify()只串行化 对象自身、可枚举的属性(不含继承)

    • Object.assign()拷贝 对象自身、可枚举属性(不含继承)

5. 回顾:判断属性是否是 对象自身的 并且 可枚举 maObject.propertyIsEnumerable(property)

  • 返回值: 只有属性 既是自身属性,又可枚举, 才返回 true

6. ES6:获取对象所有自身(不含继承)属性 的描述对象 Object.getOwnPropertyDescriptors

const obj = {
  foo: 123,
  get bar() { return ‘abc‘ }
};

const result = Object.getOwnPropertyDescriptors(obj)

console.log(result);
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

六、对象原型 操作方法

1. __proto__ 获取对象的原型对象:只有浏览器广泛支持(不推荐使用)

  • 声明: 该属性没有写入 ES6 的正文,标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署;所以在实际开发中不要使用该属性

  • 语法: obj.__proto__ 可以获取 obj 对象的原型对象

2. ES6 设置 原型对象 Object.setPrototypeOf(object, prototype)

  • 语法:
    • 参数1:被操作对象

    • 参数2:原型对象

    • 返回值:被操作对象本身

let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);

proto.y = 20;
proto.z = 40;

obj.x // 10
obj.y // 20
obj.z // 40
  • 规定:
    • 如果第一个参数不是对象,会自动转为对象;由于返回的还是第一个参数,所以这个操作不会产生任何效果
    • 若第一个参数是 undefinednull(无法转成对象),会报错
Object.setPrototypeOf(1, {}) === 1              // true
Object.setPrototypeOf(‘foo‘, {}) === ‘foo‘      // true
Object.setPrototypeOf(true, {}) === true        // true
Object.setPrototypeOf(undefined, {});   // 报错
Object.setPrototypeOf(null, {});        // 报错

3. ES6 获取 原型对象 Object.getPrototypeOf(object)

  • 语法:
    • 参数:操作对象

    • 返回值:操作对象 的 原型对象

七、关键字 super --- 指向 当前对象的原型对象(2个使用约束条件)

  • 关键字 super:指向当前对象的原型对象

  • 约束:
    • 只能用在 对象的 简写 方法 之中, 目前 super 在其他地方都会报错
// 正确用法
const obj = {
    find() {
        return super.foo;
    }
};


// 以下三种情况,都不允许,会报错
const obj = {
    foo: super.foo              // 报错:super 用在了对象的属性中
}


const obj = {
    foo: () => super.foo        // 报错:super 用在了函数中,又将这个函数定义成对象的方法
}


const obj = {
    foo: function () {
        return super.foo        // 报错:super 用在了函数中,又将这个函数定义成对象的方法   
    }
}

八、 比较 任意数据类型的值 是否相等:Object.is( )

  • ES6提出: "Same-value equality" 的原则,对比较两个值是否相等,做了更严格的规范

  • 语法:
    • 参数1,参数2: 要比较是否相等的两个值 Object.is(a, b)

    • 返回值: 布尔值

  • 对比:
    • ES6中的比较方法 Object.is( ) :NaN 等于 NaN;-0 不等于 +0

    • ES5中的比较方法 == === :NaN 不等于 NaN;-0 等于 +0;== 会发生隐式转换

以上是关于ES6 对象的扩展的主要内容,如果未能解决你的问题,请参考以下文章

ES6 对象的扩展

ES6 - 对象扩展(增强字面量)

es6~扩展运算符后续

es6数组的扩展

ES6正则扩展

函数的扩展--ES6