🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] >[success] # ES6对象新增方法 ~~~ 'ES6'引入了2个新的方法,'Object.is()' 、'Object.assign()'方法 ~~~ <br/> >[success] ## Object.is() ~~~ 在'js'中大家比较'值'的时候通常会使用'相等运算符( == )'、'严格相等运算符( === )',但是有时候 '严格相等运算符'也不准确,例如:它会认为'+0'和'-0'相等,'NaN === NaN'会返回'false',所以才会使用 'isNaN()'来检测,'ES6'引入了'Object.is()'方法来弥补'严格相等运算'符残留的怪异点 ~~~ ~~~ console.log(+0 == -0) // true console.log(+0 === -0) // true console.log(Object.is(+0, -0)) // false console.log(NaN == NaN) // false console.log(NaN === NaN) // false console.log(Object.is(NaN, NaN)) // true console.log(5 == 5) // true console.log(5 == "5") // true console.log(5 === 5) // true console.log(5 === "5") // false console.log(Object.is(5, 5)) // true console.log(Object.is(5, "5")) // false ~~~ ~~~ 在许多情况下,'Object.is()'的结果与'==='运算符是相同的,仅有的例外是:'它会认为 +0 与 -0 不相等', '而且 NaN 等于 NaN '。不过仍然没必要停止使用严格相等运算符,选 择'Object.is()',还是选择'=='或'===' ,取决于代码的实际情况。 ~~~ <br/> >[success] ## Object.assign() ~~~ 语法: Object.assign(target, ...sources) 参数: 其中`target`是目标对象,`sources`是源对象,可以有多个,返回修改后的目标对象`target`,注意:'目标对象会被修改(合并后的最终对象)' 如果'目标对象'中的属性具有'相同的键',则属性将被源对象中的属性'覆盖'。后来的源对象的属性将类似地'覆盖'早先 的'属性'。 '浅拷贝'就是拷贝'第一层'的'基本类型值',以及'第一层的引用类型地址','深拷贝'是拷贝'引用类型' ~~~ <br/> >[success] ### ES6混入 1. 类的混入 ~~~ // 创建构造函数 function EventTarget() { /*...*/ } // 给构造函数原型链添加对象 EventTarget.prototype = { constructor: EventTarget, emit: function() { /*...*/ }, on: function() { /*...*/ } } // 创建一个对象 var myObject = {} // 把EventTarget的构造函数中的原型链方法合并到myObject对象中 Object.assign(myObject, EventTarget.prototype) // 调用myObject继承过来的方法 myObject.emit("somethingChanged") ~~~ 2. 对象的混入 ~~~ 1. 'Object.assign()'方法接受'任意数量'的'供应者',而'接收者'会按照'供应者'在参数中的顺序来依次接收它们 的'属性',如果'2'者有'相似属性'则被后者的属性值'覆盖' const target = { a: 1, b: 2 }; const source = { b: 4, c: 5 }; const error = { b: 5, c: 6 }; // 接收者:target,供应者:source、error const returnedTarget = Object.assign(target, source, error); console.log(target); // { a: 1, b: 5, c: 6 } console.log(source); // { b: 4, c: 5 } console.log(error); // { b: 5, c: 6 } console.log(returnedTarget); // { a: 1, b: 5, c: 6 } // 这里的target改变,returnedTarget也会改变 target.a = 222; console.log(returnedTarget); // { a: 222, b: 5, c: 6 } ~~~ 3. 浅拷贝 ~~~ const obj = { a: 1 }; // 浅拷贝拷贝obj const copy = Object.assign({}, obj); console.log(copy); // { a: 1 } ~~~ 4. 深拷贝 ~~~ 仅限于简单的结构并且是json格式,funcction、new Date()等等都不可以使用该方法 // 源对象 obj1 = { a: 0, b: { c: 0, d: [1, 2, 3, 4] } }; // 克隆对象 let obj3 = JSON.parse(JSON.stringify(obj1)); // 修改源对象 obj1.a = 4; obj1.b.c = 4; obj1.b.d[0] = 123456; // 打印克隆对象 console.log(JSON.stringify(obj3)); // {"a":0,"b":{"c":0,"d":[1,2,3,4]}} ~~~ 5. 拷贝的对象有 symbol 类型的属性 ~~~ // 创建私有属性 let sy = Symbol('foo') // 创建对象 const o1 = { a: 1 }; const o2 = { [sy]: 2 }; // 拷贝o2到o1对象中 const obj = Object.assign({}, o1, o2); console.log(obj[sy]) // 2 console.log(obj); // { a : 1, [Symbol("foo")]: 2 } // Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。 Object.getOwnPropertySymbols(obj); // [Symbol(foo)] ~~~ 6. 继承属性和不可枚举属性是不能拷贝的 ~~~ const obj = Object.create({ foo: 1 }, { // foo 是个继承属性。 bar: { value: 2 // bar 是个不可枚举属性。 }, baz: { value: 3, enumerable: true // baz 是个自身可枚举属性。 } }); const copy = Object.assign({}, obj); console.log(copy); // { baz: 3 } ~~~ 7. 原始类型会被包装为对象 ~~~ const v1 = "abc"; const v2 = true; const v3 = 10; const v4 = Symbol("foo") const obj = Object.assign({}, v1, null, v2, undefined, v3, v4); // 原始类型会被包装,null 和 undefined 会被忽略。 // 注意,只有字符串的包装对象才可能有自身可枚举属性。 console.log(obj); // { "0": "a", "1": "b", "2": "c" } ~~~ 8. 异常会打断后续拷贝任务 ~~~ // Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象 const target = Object.defineProperty({}, "foo", { value: 1, writable: false }); // target 的 foo 属性是个只读属性。 Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 }); // TypeError: "foo" is read-only // 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。 console.log(target.bar); // 2,说明第一个源对象拷贝成功了。 console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。 console.log(target.foo); // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。 console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。 console.log(target.baz); // undefined,第三个源对象更是不会被拷贝到的。 ~~~ 9. 拷贝访问器(对象的get、set属性) ~~~ 9.1 // 这种方式是只能拷贝到返回值,并不能拷贝到访问器 const obj = { foo: 1, get bar () { return 2; } }; let copy = Object.assign({}, obj); console.log(copy); // { foo: 1, bar: 2 } copy.bar的值来自obj.bar的getter函数的返回值 9.2 // 下面这个函数会拷贝所有自有属性的属性描述符 function completeAssign(target, ...sources) { sources.forEach(source => { let descriptors = Object.keys(source).reduce((descriptors, key) => { descriptors[key] = Object.getOwnPropertyDescriptor(source, key); return descriptors; }, {}); // Object.assign 默认也会拷贝可枚举的Symbols Object.getOwnPropertySymbols(source).forEach(sym => { let descriptor = Object.getOwnPropertyDescriptor(source, sym); if (descriptor.enumerable) { descriptors[sym] = descriptor; } }); Object.defineProperties(target, descriptors); }); return target; } copy = completeAssign({}, obj); console.log(copy); // { foo:1, get bar() { return 2 } } ~~~ <br/> >[success] ### ES5混入 ~~~ 'ES5'用'mixin混入方法'可以进行对'对象'或原型上的方法进行'浅拷贝',如下: ~~~ ~~~ // 手动写一个混入方法 function mixin(receiver, supplier) { Object.keys(supplier).forEach(function (key) { receiver[key] = supplier[key] }) return receiver } // 创建构造函数 function EventTarget() { /*...*/ } // 给构造函数原型链添加对象 EventTarget.prototype = { constructor: EventTarget, emit: function () { /*...*/ }, on: function () { /*...*/ } } // 创建一个对象 let myObject = {} // 把EventTarget的构造函数中的原型链方法合并到myObject对象中 mixin(myObject, EventTarget.prototype) // 调用myObject继承过来的方法 myObject.emit("somethingChanged") ~~~ <br/> >[warning] ### 要注意的点 ~~~ 1. 需要记住'Object.assign()'并未在'接收者'上'创建访问器属性(对象的get属性、set属性...)',即使'供应者' 拥有'访问器属性'。 由于'Object.assign()'使用'赋值运算符(=)','供应者'的'访问器属性'就会转变成'接收者'的'数据属性',例如: // 创建对象 let receiver = {}, supplier = { get name() { return "file.js" } } // 把receiver混入到supplier中 Object.assign(receiver, supplier) // 获取receiver对象name属性的属性描述 let descriptor = Object.getOwnPropertyDescriptor(receiver, "name") // descriptor的自有属性的属性描述: // { // value: 'file.js', // writable: true, // enumerable: true, // configurable: true // } console.log(descriptor.value) // "file.js" console.log(descriptor.get) // undefined console.log(receiver.name) // file.js ~~~