[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
~~~
- Javascript基础篇
- Array数组
- 数组插入值
- filter()
- forEach()
- push()
- pop()
- unshift()
- shift()
- valueOf()
- 面向对象思想
- Javascript 面向对象编程(一):封装
- Javascript面向对象编程(二):构造函数的继承
- Javascript面向对象编程(三):非构造函数的继承
- 解构
- 数组的解构赋值
- 对象的解构赋值
- 函数参数解构
- 字符串的解构赋值
- 数值和布尔值的解构赋值
- 圆括号问题
- 字符串.
- split()
- charAt()
- charCodeAt()
- concat()
- indexOf()
- lastIndexOf()
- match()
- replace()
- includes()
- 初识递归
- 渲染ul-li树形结构
- 异步函数解决方案
- 1. callback回调函数
- 2. ES6 - Promise
- JavaScript高级程序设计(书)
- 在html中使用JavaScript
- script标签的位置
- 延迟脚本
- 异步脚本
- <noscript>元素
- 基本概念
- 严格模式
- 变量详解
- 数据类型
- typeof操作符
- undefined类型
- Null类型
- Boolean类型
- Number类型
- 深入了解ES6(书)
- var 、let 、 const
- 字符串与正则表达式
- 字符串
- 正则表达式
- 函数
- 函数形参默认值
- 使用不具名参数
- 函数构造器的增强能力
- 扩展运算符
- name属性
- 明确函数的多重用途
- 块级函数
- 箭头函数
- 尾调用优化
- 扩展的对象功能
- 对象类别
- 对象字面量语法的扩展
- ES6对象新增方法
- 重复的对象属性
- 自有属性的枚举顺序
- 更强大的原型
- 解构:更方便的数据访问
- 为什么要用解构?
- 对象解构
- 数组解构
- 混合解构
- 参数解构
- Symbol与Symbol属性
- 创建Symbol
- Symbol的使用方法
- Symbol全局私有属性
- Symbol与类型强制转换
- Symbol属性检索
- Symbol的一些构造方法
- Set集合与Map集合
- Set集合
- Weak Set集合(弱引用Set集合)
- Map集合
- JS标准内置对象
- Object 构造函数及属性
- Object 构造方法
- Symbol 内建对象类的函数及属性
- Set 构造函数及属性
- Weak Set 构造函数及属性
- JS杂项
- 类数组对象
- Class类的理解和使用