definePropety
?顾名思义,这个方法就是给对象添加属性的方法,属于es6的知识点。
默认情况下,使用?Object.defineProperty() ?添加的属性值是不可修改(immutable)的。?
这个方法传递三个参数,分别是 给? 哪个对象? 添加? 哪个属性,且 这个属性需要做什么配置。
配置项存在四个属性,分别是 value,enumerable,writable,configurable
?value:该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为?undefined。
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {})
console.log(person, 'person')
??
?给定一个值之后
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {
value: 22
})
?
但是这个age属性,颜色较浅,这又是为什么呢?
for (const i in person) {
console.log(i, 'i')
}
通过 遍历之后,我们发现,控制台上只有 原先的两个属性,新增加的 age 属性,则是没有被遍历到,这是因为??defineProperty 的 第三个参数配置项,有一个默认配置项? enumerable 会控制属性是否能被枚举?
enumerable :? ?当且仅当该属性的?enumerable ?键值为?true ?时,该属性才会出现在对象的枚举属性中。默认为?false 。所以在for...in? 遍历 或者 Object.keys()? 遍历时,才会无法打印出来
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {
value: 22,
enumerable: true
})
console.log(person, 'person')
for (const i in person) {
console.log(i, person[i])
}
?
可以看到,我将 默认的??enumerable : false? 改为了 true ,遍历的时候,控制台上就展示了所有的属性,且 person 对象属性颜色一致,这就是配置 为 是否可枚举的属性
?writable:这个就是控制新增的属性是否可以修改,默认为false不能修改属性值
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {
value: 22,
enumerable: true,
// writable:true
})
console.log(person, 'person') // {name: '张三', six: '男', age: 22}
person.age = 18
console.log(person, 'person') // {name: '张三', six: '男', age: 22}
// 改变属性值无效
?设置为 true 之后,就变成了可修改的属性
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {
value: 22,
enumerable: true,
writable:true // 控制是否能改变属性值
})
console.log(person, 'person') // {name: '张三', six: '男', age: 22}
person.age = 18
console.log(person, 'person') // {name: '张三', six: '男', age: 18}
// 改变了属性值
?
?configurable :控制属性是否能被删除,默认是 false 不能被删除的
?通过正常方式定义的对象属性,是可以删除的,例如下面的 通过字面量创建的 person 对象属性
// 定义对象
let person = {
name: '张三',
six: '男'
}
console.log(person, 'person')
delete person.name
console.log(person, 'person')
?
?但是,通过?defineProperty 添加的属性,且?configurable:?false 之后,该属性则不能被删除
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {
value: 22,
enumerable: true,
writable: true,
configurable: false //默认为false,不能删除该属性
})
console.log(person, 'person')
delete person.age
console.log(person, 'person')
?
但是,将?configurable: true? 之后,该属性则是可以删除的了
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {
value: 22,
enumerable: true,
writable: true,
configurable: true
})
console.log(person, 'person')
delete person.age
console.log(person, 'person')
?
??
除了这四个用来配置属性的方法,还存在两个读取的方法,分别是 get() 和 set() 两个函数
?get() : 属性的 getter 函数,如果没有 getter,则为?undefined 。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入?this ?对象(由于继承关系,这里的this 并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。默认为?undefined。
?下面这段代码,我向? person 对象内部添加了 age 属性,且值为 18,该属性,可枚举、可修改、可删除,然后我接着写了 get() 方法,用来做读取这个操作,也就是访问这个 age 属性时,返回一个值,这个时候就出问题了。
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {
value: 18, //默认 undefined 属性值
enumerable: true, // 默认 false ,禁止枚举
writable: true, // 默认 false ,禁止修改
configurable: true, // 默认 false , 禁止删除
// get函数( 通常叫做 getter ),当访问该属性时,会自动调用此函数,
// 默认传入this对象,函数的返回值会被作为 该属性的值
get() { return 'bValue'; },
})
console.log(person, 'person')
?这个错误解释一下就是:?使用Object.defineProperty() 定义对象属性时,如已设置 set 或 get, 就不能设置 writable 和 value 中的任何一个了,所有关于value的操作都由get和set代理了。
?所以我们需要把 value 和??writable? 属性删除,如下
Object.defineProperty(person, 'age', {
// value: 18, //默认 undefined 属性值
enumerable: true, // 默认 false ,禁止枚举
// writable: true, // 默认 false ,禁止修改
configurable: true, // 默认 false , 禁止删除
// get函数( 通常叫做 getter ),当访问该属性时,会自动调用此函数,
// 默认传入this对象,函数的返回值会被作为 该属性的值
get() { return 'bValue'; },
})
?结果如下,三个属性都是存在的,此时 age 属性 的值没有展示出来,且多了一个 get age() 这个方法,我们点击 age 的属性值,发现 和我们在 get 函数内部返回的值 是一样的
?这就印证了,为什么不让写 value 这个属性,因为 get 函数 返回的值,默认成为了 value 的值
那么?writable 对应的 set 又是什么样的呢?
set()? :属性的 setter 函数,如果没有 setter,则为?undefined 。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的?this ?对象。 默认为?undefined。
Object.defineProperty(person, 'age', {
// value: 18, //默认 undefined 属性值
enumerable: true, // 默认 false ,禁止枚举
// writable: true, // 默认 false ,禁止修改
configurable: true, // 默认 false , 禁止删除
// get函数( 通常叫做 getter ),当访问该属性时,会自动调用此函数,
// 默认传入this对象,函数的返回值会被作为 该属性的值
get() { return 'bValue'; },
// 当修改 age 的值时,set函数( settter )就会被调用,且会受到修改的具体值
set(x) {
console.log('age被修改了,且值是,', x)
}
})
set函数被调用时,会触发这个 console,我们来看看控制台上输出了什么
在改变了 age 属性的值之后,我们看到控制台上打印出了set()函数内部的console,但是我们再次输出 person 对象的时候,发现 person 对象中 age 属性值并没有发生改变,这是因为,我们只是接收到了需要改变的值,但是并没有将新的值 赋值 给 age 属性,但是如果我们直接 赋值,则会出现下面的情况
?1、直接赋值
Object.defineProperty(person, 'age', {
// value: 18, //默认 undefined 属性值
enumerable: true, // 默认 false ,禁止枚举
// writable: true, // 默认 false ,禁止修改
configurable: true, // 默认 false , 禁止删除
// get函数( 通常叫做 getter ),当访问该属性时,会自动调用此函数,
// 默认传入this对象,函数的返回值会被作为 该属性的值
get() { return 'bValue'; },
// 当修改 age 的值时,set函数( settter )就会被调用,且会收到修改的具体值
set(x) {
console.log('age被修改了,且值是,', x)
age = x
}
})
?
此时我们会发现,这改了和没改一样,根本没生效啊,这是因为 set() 函数内部的 age ,根本就不是 我们通过 definepropetry() 新增的age 属性
2、通过this.age 赋值
?因为之前说过,set 和 get 会默认将 this 传递进去,所以,我们使用 this.age 来赋值。
Object.defineProperty(person, 'age', {
// value: 18, //默认 undefined 属性值
enumerable: true, // 默认 false ,禁止枚举
// writable: true, // 默认 false ,禁止修改
configurable: true, // 默认 false , 禁止删除
// get函数( 通常叫做 getter ),当访问该属性时,会自动调用此函数,
// 默认传入this对象,函数的返回值会被作为 该属性的值
get() { return 'bValue'; },
// 当修改 age 的值时,set函数( settter )就会被调用,且会收到修改的具体值
set(x) {
console.log(this, 'this')
console.log('age被修改了,且值是,', x)
}
})
在调用 set() 方法之后,我们能看到,打印出的 this 内部,是存在 age 属性的,所以按道理来说,我们使用 this.age = x 来说是能成功修改 age 的属性值的
?但是实际上,在我进行赋值操作之后,出现了一个我没想到的问题
Object.defineProperty(person, 'age', {
// value: 18, //默认 undefined 属性值
enumerable: true, // 默认 false ,禁止枚举
// writable: true, // 默认 false ,禁止修改
configurable: true, // 默认 false , 禁止删除
// get函数( 通常叫做 getter ),当访问该属性时,会自动调用此函数,
// 默认传入this对象,函数的返回值会被作为 该属性的值
get() { return 'bValue'; },
// 当修改 age 的值时,set函数( settter )就会被调用,且会收到修改的具体值
set(x) {
console.log('age被修改了,且值是,', x)
this.age = x
}
})
?
?set() 方法被调用了很多很多次,直到最后堆栈溢出才停止调用,这说明这样也是不可取的,那么,我想修改属性的值,应该怎么做呢?
其实很简单,我们在外面定义一个变量,用来存储 age 的属性值,然后通过 get() 函数将 number的值返回出去,这样的话,在外部访问 age 属性时,就会走 get() 函数,然后 将当前的 number 的值返回出去,那么在修改的时候,就会调用 set()? 方法,然后动态的改变 number 的值
// 定义 number
let number = 18
// 定义对象
let person = {
name: '张三',
six: '男'
}
Object.defineProperty(person, 'age', {
// value: 18, //默认 undefined 属性值
enumerable: true, // 默认 false ,禁止枚举
// writable: true, // 默认 false ,禁止修改
configurable: true, // 默认 false , 禁止删除
// get函数( 通常叫做 getter ),当访问该属性时,会自动调用此函数,
// 默认传入this对象,函数的返回值会被作为 该属性的值
get() { return number },
// 当修改 age 的值时,set函数( settter )就会被调用,且会收到修改的具体值
set(x) {
console.log('age被修改了,且值是,', x)
number = x
}
})
?
?本章总结
1、defineProperty 方法接收三个参数,第一个参数是需要改变的对象,第二个参数是,需要添加的属性,第三个参数则是一个可配置的 json对象,用来配置 添加的属性
2、配置对象内部存在四个属性和两个方法
四个属性分别是
-
value: xxx,??????????// 设置新增属性的值,默认?undefined?属性值,可以是任何类型的值 -
enumerable:?true,???// 设置新增属性是否能被枚举,默认?false??,禁止枚举 -
writable:?true,?????// 设置新增属性是否能被修改,默认?false?,禁止修改 -
configurable:?true,??// 设置新增属性是否能被删除,默认?false?,?禁止删除
两个方法分别是
- get(){} : get函数(?通常叫做?getter?),当访问该属性时,会自动调用此函数,默认传入this对象,函数的返回值会被作为?该属性的值
- set(){} :?当修改?age?的值时,set函数(?settter?)就会被调用,且会收到修改的具体值
ps : 一般会将新增的属性的属性值,通过一个变量定义在外部,而不是直接写死,这是因为,在set() 的时候,无法同步修改 该属性值。定义在外的属性值,其实和 需要改变的对象 是两个不想管的东西,但是通过??defineProperty? 连接了起来,你要访问属性值,我就去给你现取,你想修改属性值,我也给你同步修改 外部变量的值,想访问新的值了,再去调用 新的 get() 取到新的值返回
|