类属性类型
- 数据属性
有4个特性描述他们的行为。
- Configurable:属性是否可以通过delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认为true。
2.Enumerable:表示属性是否可以通过for-in 循环返回。默认为true。
3.Writable:表示属性的值是否可以被修改。默认为true。
4.Value:包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为undefined.
//对象字面量创建对象。对象包含name,age,job属性和sayName方法
let person = {
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName() {
console.log(this.name);
}
};
要修改属性的特性就要使用Object.defineProperty(添加属性对象,属性名称,描述符对象(configurable、enumerable、writable 和value))方法。
let person = {};
Object.defineProperty(person, "name", {
writable: false,
value: "Nicholas"
});
console.log(person.name); // "Nicholas"
person.name = "Greg";
console.log(person.name); // "Nicholas" 严格模式下回抛出错误。因为writable属性被改为只读了。
- 访问器属性
有4个特性描述他们的行为。
- Configurable:属性是否可以通过delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认为true。
2.Enumerable:表示属性是否可以通过for-in 循环返回。默认为true。
3.Get:获取函数,在读取属性时调用。默认值为undefined。
4.Set:设置函数,在写入属性时调用。默认值为undefined。
访问器属性是不能直接定义的,必须使用Object.defineProperty()
// 定义一个对象,包含伪私有成员year_和公共成员edition
let book = {
year_: 2017,
edition: 1
};
Object.defineProperty(book, "year", {
get() {
return this.year_;
},
set(newValue) {
if (newValue > 2017) {
this.year_ = newValue;
this.edition += newValue - 2017;
}
}
});
book.year = 2018;
console.log(book.edition); // 2
合并对象
Object.assign():。这个方法接收一个目标对象和一个或多个源对象作为参数,然后将每个源对象中可枚举(Object.propertyIsEnumerable()返回true)和自有(Object.hasOwnProperty()返回true)属性复制到目标对象。
let dest, src, result;
/**
* 简单复制
*/
dest = {};
src = { id: 'src' };
result = Object.assign(dest, src);
// Object.assign 修改目标对象
// 也会返回修改后的目标对象
console.log(dest === result); // true
console.log(dest !== src); // true
console.log(result); // { id: src }
console.log(dest); // { id: src }
/**
* 多个源对象
*/
dest = {};
result = Object.assign(dest, { a: 'foo' }, { b: 'bar' });
console.log(result); // { a: foo, b: bar }
Object.assign()实际上对每个源对象执行的是浅复制(浅拷贝)。如果多个源对象都有相同的属性,则使用最后一个复制的值。
Object.assign(target,source):可以实现第一层的“深拷贝”,但无法实现多层的深拷贝
以当前A对象进行说明
第一层“深拷贝”:就是对于A对象下所有的属性和方法都进行了深拷贝,但是当A对象下的属性如data是对象时,它拷贝的是地址,也就是浅拷贝,这种拷贝方式还是属于浅拷贝。
多层深拷贝:能将A对象下所有的属性,及时属性是对象,也能够深拷贝出来,让A和B相互独立,这种叫才叫深拷贝。
var A={
name:"martin",
data:{num:10},
say:function(){
console.log("hello world")
}
}
var B={}
Object.assign(B,A); //将A拷贝到B
B.name="lucy";
console.log(A.name); //martin,发现A中name并没有改变
B.data.num=5;
console.log(A.data.num); //5,发现A中data的num属性改变了,说明data对象没有被深拷贝
直接赋值:这种方式实现的就是纯粹的浅拷贝,B的任何变化都会反映在A上。
Object.assign():这种方式实现的实现的是单层“深拷贝”,但不是意义上的深拷贝,对深层还是实行的浅拷贝。
浅拷贝,深拷贝
浅拷贝:浅拷贝只复制一层对象的属性。
深拷贝:深拷贝则递归复制了所有层级。
数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。
1、基本数据类型的特点:直接存储在栈(stack)中的数据
栈内存 | 栈内存 |
---|---|
name | val |
age | 10 |
2、引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里
栈内存 | 栈内存 | 堆内存 |
---|---|---|
name | val | val |
arr | 堆地址(指向堆内存的值) | [1,2,3,4] |
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
浅拷贝只复制指向某个对象的指针(引用地址),而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
对象标识及相等判断
ES6新增Object.is(),该方法和===很像,但完善了边界情况。
// 这些是===符合预期的情况
console.log(true === 1); // false
console.log({} === {}); // false
console.log("2" === 2); // false
// 这些情况在不同JavaScript 引擎中表现不同,但仍被认为相等
console.log(+0 === -0); // true
console.log(+0 === 0); // true
console.log(-0 === 0); // true
// 要确定NaN 的相等性,必须使用极为讨厌的isNaN()
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true
为改善这类情况,ECMAScript 6 规范新增了Object.is(),这个方法与===很像,但同时也考虑
到了上述边界情形。这个方法必须接收两个参数:
console.log(Object.is(true, 1)); // false
console.log(Object.is({}, {})); // false
console.log(Object.is("2", 2)); // false
// 正确的0、-0、+0 相等/不等判定
console.log(Object.is(+0, -0)); // false
console.log(Object.is(+0, 0)); // true
console.log(Object.is(-0, 0)); // false
// 正确的NaN 相等判定
console.log(Object.is(NaN, NaN)); // true
要检查超过两个值,递归地利用相等性传递即可:
function recursivelyCheckEqual(x, ...rest) {
return Object.is(x, rest[0]) &&
(rest.length < 2 || recursivelyCheckEqual(...rest));
}