大家好,我是练习时长两年半的JS练习生。
你真的理解对象吗?
带着这个问题,我决定以红宝书为基石,进行一场JS回访之旅,第一站,来拜访对象
1.属性的类型
属性也有类型? 不就键和值吗? 非也!属性分为两种:数据属性和访问器属性
1.1数据属性
数据属性,通俗一点就是所谓的键(key)。但是我们今天关注它的四个特性,这四个特性描述了四种行为。我们想探索这四种特性,就得使用Object.defineProperty()来验证。这个方法接收3个参数:要给其添加属性的对象、属性的名称和一个描述符对象(我们的四个特性就写在这里面)
const oldOBj = {
name:'gjh'
}
console.log(oldOBj.name) // gjh
// 上面的这段代码可以用下面的代码来描述
let newOBj = {}
Object.defineProperty(newOBj,'name',{
configurable:true,// 允许对象里的name属性 可以被删除
enumerable:true, // 允许对象里的name属性 可以被遍历返回
writable:true, // 允许对象里的namne属性 可以被修改
value:'gjh' // 这里就是name属性对应的值
})
console.log(newOBj.name) // gjh
那么我们可以逆向思维来测试它的这个特性。
Object.defineProperty(newOBj,'name',{
configurable:false,// 不允许对象里的name属性被删除
enumerable:false, // 不允许对象里的name属性被遍历返回
writable:false, // 不允许对象里的namne属性被修改
value:'gjh' // 这里就是name属性对应的值
})
console.log(newOBj.name) // gjh
delete newOBj.name // 因为我们的configurable的特性是false 所以你删除不掉它
console.log(newOBj.name) // gjh
newOBj.name = 'zh' // 我们将writable设置为false 所以也无法去修改它的value
console.log(newOBj.name) // gjh
通过上面的例子,我们重新认识了对象。原来它也是有生命的,也是有增删改查的特性。
1.2访问器属性
访问器属性我理解为是改和查。接下来我会用一个例子,来定义一个私有属性,并且用访问器get和set来实现改变一个属性如何去影响另一个属性。
let book = {
year_:24, // 属性加下划线 表示它是私有的
edition:1
}
Object.defineProperty(book,'year',{
get:() =>this.year_, //这是es6的写法
set(newValue) {
if(newValue > 24) {
this.edition += newValue - this.year_
this.year_ = newValue;
}
}
})
book.year = 23
console.log(book.edition) // 1 条件没有被满足 因此输出1
book.year = 25
console.log(book.edition) // 2 满足条件 因此输出2
我们将上面的代码更加原始化的体现一下
let newbook = {}
Object.defineProperty(newbook,
{
year_:{
value:24
},
edition:{
value:1
},
year:{
get:() =>this.year_, //这是es6的写法
set(newValue) {
if(newValue > 24) {
this.edition += newValue - this.year_
this.year_ = newValue;
}
}
}
})
ok,到目前为止,我们才终于初步理解了对象。
1.3 获取对象属性的描述特征
上面我们用代码去体验了属性的特征。 那么我们拿到别人设计的对象,如何去知道它有哪些特征呢? 使用getOwnPropertyDescriptor().
let book = {};
Object.defineProperties(book, {
year_: {
value: 2017
},
edition: {
value: 1
},
year: {
get: function() {
return this.year_;
},
set: function(newValue){
if (newValue > 2017) {
this.year_ = newValue;
this.edition += newValue -2017;
}
}
}
});
console.log(Object.getOwnPropertyDescriptors(book));
// {
// edition: {
// configurable: false,
// enumerable: false,
// value: 1,
// writable: false
// },
// year: {
// configurable: false,
// enumerable: false,
// get: f(),
// set: f(newValue),
// },
// year_: {
// configurable: false,
// enumerable: false,
// value: 2017,
// writable: false
// }
// }
1.4 对象合并方法Object.assign() 与 ES6的{...obja,...objb} 的不同之处
1.4.1:大家观察dest 和 src 。在经过了assign 合并后 dest对象竟然也被改变了 。我之前以为它两合并成一个新的对象。没成想 首位对象本身也会被改变。
1.4.2:合并后dest和result 是不是用的一个引用地址?
1.4.3: ES6的合并 并不会去改变首位对象
let dest, src, result;
/**简单复制 */
dest = {name:'gjh'};
src = { id: 'src' };
result = Object.assign(dest, src);
// Object.assign修改目标对象
// 也会返回修改后的目标对象
console.log(dest === result); // true
console.log(dest === src); // false
console.log(result); // { name: 'gjh', id: 'src' }
console.log(dest); // { name: 'gjh', id: 'src' }
console.log(src)
/**
* ES6的写法
*/
let a,b,c;
a = {a_name:'a'};
b = {b_name:'b'};
c = {...a,...b};
console.log(a) // { a_name: 'a'}
console.log(b) // { b_name: 'b' }
console.log(c) // { a_name: 'a', b_name: 'b' }
1.5 可计算属性
换句话来说 就是属性名字也是可以通过值传进去的,在某些场景下,比如你去写自定义组件的或者去vue的mixins复用方法时,这个知识点就很用的上,下面我用ES6的语法给大家介绍一下如何实现
const keyList = ['name','age']
const valueList = ['gjh',24]
const my_obj = keyList.reduce((pre,keyName,inx) => {
let newObj = {[keyName]:valueList[inx]}
return {...newObj,...pre}
},{})
console.log(my_obj) // { age: 24, name: 'gjh' }
1.6 对象解构
结构运算符 比较牛逼的地方在于。如果你结构出来的值,不存在的话 它并不会报错
const keyList = ['name','age']
const valueList = ['gjh',24]
const my_obj = keyList.reduce((pre,keyName,inx) => {
let newObj = {[keyName]:valueList[inx]}
return {...newObj,...pre}
},{})
const tarObj = {son:{niubiname:'niubi'}}
const niubiObj = {...my_obj,...tarObj}
console.log(niubiObj) // { age: 24, name: 'gjh', son: { niubiname: 'niubi' } }
// 结构从这里开始
const {
age,
name,
son:{
niubiname // 这里是多层结构
},
gjh // 注意看 这个属性并不存在于我们的对象中
} = niubiObj
console.log(niubiname) // 这里是多层结构
console.log(gjh) // 不存在的属性 并不会报错 它会报undefined