ES5标准中的Object.create() 和 Object.defineProperties()
JohnKeatinghhh
ES5标准中的Object.create和Object.defineProperties
- Object.create(prototye,[description])
- Object.defineProperties(object,descriptors)
Object.create(prototye,[description])
【作用】:以指定对象为原型,创建新的对象
【参数】:
- prototype: 就是要作为原型的那个对象;
- description 为新的对象指定新的属性,并对属性进行描述,可用的关键字:
– value:新属性的值
– writable: 是否可以修改,默认false
– configurable: 是否可以删除,默认false
– enumerable: 当前属性是否可以被for… in… 枚举到,默认false
– get:属于accessors的一种,值是获取当前属性指的回调函数 (注意accessors与value和writable这两个冲突,逻辑上也很好理解)
– set:属于accessors的一种,值是修改当前属性值触发的回调函数,并且实参即为修改后的值 (注意accessors与value和writable这两个冲突,逻辑上也很好理解)
【栗子】:
var obj = {country : 'CN',race : 'HUM'}var new_obj = Object.create(obj,{// 最外层对象,键是要创建的新属性名称,值是一个对象,包括了value,writable等属性name:{value:'蔡徐坤',enumerable:true},gender:{value:'女',writable:true, //可以修改性别configurable:true, //可以不要性别enumerable:false //不要暴露自己的性别}})new_obj.name = 'ikun' //名字改不了new_obj.gender = '男' //厉不厉害你鲲哥for(var i in new_obj){console.log(new_obj[i])}
上面这个例子以我国著名音乐家、舞蹈家、篮球运动员蔡徐坤先生为例。
首先设置一个obj父类,包含了country属性和race属性,然后通过调用Object方法,以obj为原型构造一个新的对象new_obj,并定义两个新的属性:name和gender,而 这两个属性也分别是两个对象,对象中包括了一些性质:
其中name的writable和configurable默认是false,颇有行不更名坐不改姓的高风亮节;
而gender的writable和configurable则设为true,展现了她艺术人格中灵活多变的一面。
定义完new_obj之后,尝试修改姓名为'ikun',由于她行不更名坐不改姓的艺术风骨,这个改动失败了。
而new_obj.gender = '男'则展现了性别的灵活多变,令人目不暇接。
最后通过for枚举new_obj的属性,会打印出name,country和age,唯独将gender神秘的一面留给了大家。(因为enumerable是false)
Object.defineProperties(object,descriptors)
【作用】:为指定对象扩展多个属性
【参数】:
- object: 要扩展属性的对象;
- descriptors 要扩展的属性,并对属性进行描述,可用的关键字:
– value:新属性的值
– writable: 是否可以修改,默认false
– configurable: 是否可以删除,默认false
– enumerable: 当前属性是否可以被for… in… 枚举到,默认false
– get:属于accessors的一种,值是获取当前属性指的回调函数 (注意accessors与value和writable这两个冲突,逻辑上也很好理解)
– set:属于accessors的一种,值是修改当前属性值触发的回调函数,并且实参即为修改后的值 (注意accessors与value和writable这两个冲突,逻辑上也很好理解)
【栗子】:
由于上一个例子演示了value和writable,这个例子就演示下get 和 set。value/writable和 get/set是冲突的两组属性。
如果属性A设置了value/writable,那么这个属性A的属性可以直接通过obj.A访问,但是这样定义属性会有一个不方便的地方,那就是需要用到对象中其他属性的时候:
假设这样一个情景,一个对象的fullName属性需要通过firstName和lastName拼接而成,那么在定义的时候可能会写出这样的错误代码:
var obj3 = {firstName : 'kobe',lastName : 'bryant',fullName : this.firstName + this.lastName}console.log(obj3)
最后的结果,obj3的fullName属性是NaN,为什么?
因为定义这个对象的时候,this指向的是window作用域,window里自然是没有firstName和lastName的,而我们用Object.defineProperties则可以解决这个问题:
var obj2 = {firstName: 'kobe',lastName: 'bryant'}Object.defineProperties(obj2, { // 最外层对象,囊括要设置的属性fullName: {get: function () { //获取扩展属性的回调函数return this.firstName + ' ' + this.lastName},set: function (data) { //监听扩展属性的回调函数,当扩展的属性发生变化时会自动调用//此时扩展的属性的新值会作为实参传入到这个回调函数中dataArr = data.split(' ')this.firstName = dataArr[0]this.lastName = dataArr[1]},// value: 'shabi',// writable: true,// configurable:true,enumerable: true}})obj2.fullName = 'steve jobs'for (var i in obj2) {console.log(obj2[i]) // steve jobs}
通过get属性绑定一个回调函数,来获取fullName属性。可能大家会有个疑问:为什么get回调函数里的this就指向obj2本身呢?这个就是get 底层的逻辑了。这个get实际上是一个异步的过程,每当 fullName成为右值的时候,get就会被调用。
同理,set通过绑定一个回调函数来监听 fullName的变化。每当fullName成为左值的时候,set方法就会别调用,并且会将赋给左值的东西(这个案例里是'steve jobs'字符串)作为实参(对应的形参就是data)传入到set函数中。在set函数里通过分割字符串分别修改firstName和lastName,这样下次再获取fullName的时候,就能根据新的firstName和lastName计算出新的fullName,也就是实现了对fullName的更新。