上次介绍JavaScript简单的创建对象方式:https://blog.csdn.net/cleanHtroop/article/details/103842461
“JavaScript中万物皆为对象”,这是一个错误的说法。
事实上,JavaScript中包含有基本类型(string, boolean, number, null, undefined 和 symbol)。所以并不是所有都是对象。
判断对象
如果判断是否是一个对象,最常用的方法是 typeof
typeof {} // "object"
但typeof也有小缺陷,就是null会被当成对象类型
typeof null // "object"
如果需要更准确的判断方式,可以使用下面的方式去判断
function isObject(value) {
return Object.prototype.toString.call(value) === "[object Object]"
}
isObject({}) // true
isObject(null) //false 因为Object.prototype.toString.call(null) === "[object Null]"
输出结果主要根据[[Class]]的内部属性,但这个属性不能直接访问,只能通过上面的方式来查看。[[Class]]与原生对象构造函数相对应,但是null和undefined除外,因为Null()和Undefined()的原生构造函数不存在,但是内部的[[Class]]属性仍然是”Null”和”Undefined”
对象属性和方法
var person = {
name: "Howard",
age: 20,
job: "Front-End Engineer",
sayName: function() {
console.log(this.name);
}
}
读取对象属性和调用对象方法的有两种形式:
person.name // "Howard"
person['name'] // "Howard"
最常用的第一种方式,但第二种方式可以支持变量的方式
var value = 'name';
person.value // undefined
person[value] // "Howard"
实际上,获取对象属性的时候,会触发[[Get]]操作,检查是否有名称相同的属性,如果找到就返回该值,没有就去查找prototype。当然,修改属性的时候就会触发[[Put]]操作,会进行属性描述符判断(可以见下文)。
我们可以对[[Put]]和[[Get]]操作进行修改。
var obj = {
get a() {
return 2;
}
};
Object.defineProperty(
obj, //对象
'b', //属性名
{ //描述符
get: function() {return this.a * 2}
}
)
obj.a //2
obj.b //4
var obj = {
get a() {
return this._a_;
},
set a(val) {
this._a_ = val * 2
}
};
obj.a = 2;
obj.a //4
即使给obj赋值a,在读取属性的时候,也会使用修改的get方法
obj.a = 3;
obj.a // 2
复制对象
在Java里面,会提供一个clone的方法来帮助我们进行对象复制。但是JavaScript里面没有。
大部分面向对象语言,对于复制对象来说,分为深复制和浅复制,因为变量保存的是对象的引用,所以不小心就会导致浅复制:
var boy = {
name: 'Boy',
person: person
}
var girl = {
name: 'Girl',
person: person
}
boy.person.age = 10;
girl.person.age // 10
不仅仅是普通对象,常用的内置对象--数组,也容易出现这样的错误。
在ES6中,提供了一个非常常用的方法,经常用于React, angular等框架的代码中, Object.assign(target, ...sources),会返回一个新的对象引用,这样框架就可以进行脏值检测;
var boy = Object.assign({ name: ‘Boy’ }, person) // {name: "Howard", age: 10, job: "Front-End Engineer", sayName: ƒ}
对于深复制,需要解决问题有很多,例如如何解决相互循环引用,怎么复制一个函数等,会涉及不同引擎对于不同类型函数的处理方式
许多框架都有深复制的方法,例如jQuery的$.extend(true,object1, object2);
如果确保JSON安全(就是可以序列成一个JSON字符串并且根据这个字符串解析出一个结构和值完全一样的对象),可以这个方法:
var newObj = JSON.parse(JSON.stringify(someObj));
var person = {
name: "Howard",
age: 20,
job: "Front-End Engineer",
sayName: function() {
console.log(this.name);
},
mon: {
age: 50
}
}
var boy = JSON.parse(JSON.stringify(person))
boy.mon.age = 40
person.mon.age // 50
属性描述符
在ES5之前,没有检测属性的方法,但ES5之后,所有属性都具有属性描述符,但是比较少使用到。只列出来。
1. Object.getOwnPropertyDescriptor(obj, prop) 方法返回指定对象上一个自有属性对应的属性描述符。
2. Object.getOwnPropertyDescriptors(obj) 方法用来获取一个对象的所有自身属性的描述符。
3. Object.defineProperties(obj, props) 方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
4. Object.defineProperty(obj, prop, descriptor) 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
属性描述符有:
- Writable // 决定是否可以修改属性的值
- Configurable //决定属性是否可以使用defineProperty方法去修改属性描述符
- Enumerable // 决定属性是否在对象的属性枚举中 例如for…in循环
a.常量
当设置writable:false 和 configurable:false可以创建一个对象常量
b.禁止拓展
Object.preventExtensions(obj) // 不能添加新属性
c.密封
Object.seal() //调用perventExtension方法,并且设置configurable: false
d.冻结
Object.freeze() //调用seal方法,并且设置writable: false
tip: Object.assign也是无法拷贝属性描述符的,MDN提供这个方法去进行拷贝。
Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
参考文献
《你不知道的JavaScript上卷》第三章对象
《JavaScript高级程序设计(第三版)》p596~p597