基础巩固,深入JS对象属性

在我们平常的使用中,属性对我们来说只是一个简单的”键值对”,但对象属性实际上还有更多强大的东西。

先来看一个例子

let user = {
    name: 'Steven'
}
复制代码

声明了一个user对象,name就是它的一个属性,’Steven’是属性值(value)
但其实,这只是表面的东西,里面其实还有属性标志和属性描述符。
那么什么是属性标志和属性描述符呢?

属性标志

对象属性(properties),除 value 外,还有三个特殊的特性(attributes),也就是所谓的“标志”:

  • writable — 是否可写,如果为 true,则值可以被修改,否则它是只可读的。
  • enumerable — 是否可枚举,如果为true,则会被在循环(for..in)或Object.keys()中列出,否则不会被列出。
  • configurable — 是否可配置,如果为true,则此特性可以被删除,这些属性也可以被修改,否则不可以。

当我们创建一个属性时,它们默认为true。
好,现在我们知道了什么是属性标志,那么如何获取呢?

属性标志的获取

方法是: Object.getOwnPropertyDescriptor(obj, propertyName)

第一个参数是需要从中获取信息的对象
第二参数是属性的名称

let user = {
    name: 'Steven'
}
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
console.log(descriptor);
/* 输出结果,属性描述符
{
  value: 'Steven', 
  writable: true,  
  enumerable: true,
  configurable: true
}
*/
复制代码

这里输出的结果就是属性描述符了,可以看到它们全都都是true,现在我们来修改它们

属性标志的操作方法

Object.defineProperty
修改单个属性标志的方法是Object.defineProperty(obj, propertyName, descriptor)

obj:修改的对象
propertyName:修改的属性
descriptor:要应用的属性描述符对象

看个例子就明白怎么用了:

let user = {
    name: 'Steven'
}
Object.defineProperty(user, 'name', {
  value'史蒂文',
  writablefalse,
  enumerablefalse,
  configurablefalse
})
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
console.log(descriptor);
/* 输出结果,属性描述符
 {
  value: '史蒂文',
  writable: false,
  enumerable: false,
  configurable: false
}
*/
复制代码

这里我一次性修改了四个属性描述符,当然你可以只修改你想修改的那一个或多个都行。
可以看出,四个值都被成功修改了。如果想一次性修改多个属性,就可以用下面的方法。

Object.defineProperties
 Object.defineProperties(obj, descriptors)允许一次定义多个属性。

obj:要修改的对象
descriptors:多个属性描述符

用法如下:

let user = {
  name'Steven',
  age22
}
Object.defineProperties(user, {
  name: {value'史蒂文'writablefalse},
  age: {value18writablefalse}
})
复制代码

这里只修改了 value 和 writable。所以,我们是可以一次性设置多个属性的。
而要一次获取所有属性描述符,我们可以使用Object.getOwnPropertyDescriptors(obj)方法。
这里就不在演示了。

当然了,这两个方法不仅仅可以修改已有的属性,也可以动态的添加没有的属性。

let user = {
  name'Steven',
  age22
}
Object.defineProperty(user, 'job', {
  value'teacher',
  writabletrue,
  enumerabletrue,
  configurabletrue
})
console.log(user); 
// { name: 'Steven', age: 22, job: 'teacher' }
复制代码

可以看到成功添加了一个job属性。

属性的getter 和 setter

有两种类型的对象属性。

第一种是 数据属性。我们已经知道如何使用它们了。到目前为止,我们使用过的所有属性都是数据属性。

第二种类型的属性是新东西。它是 访问器属性(accessor properties)。它们本质上是用于获取和设置值的函数,但从外部代码来看就像常规属性。

getter 和 setter

访问器属性由 “getter” 和 “setter” 方法表示,在对象字面量中,它们用 get 和 set 表示:

let obj = {
  get userName() {
    // 当读取 obj.userName 时,getter 起作用
  },
  set userNmae() {
    // 当执行 obj.userNmae = value 操作时,setter 起作用
  }
}
复制代码

当读取 obj.userName 时,getter 起作用,当 obj.userNmae 被赋值时,setter 起作用。
例如,我们有一个具有 firstName 和 lastName 属性的对象 user:

let user = {
  firstName'Lebron',
  lastName'James',
}
复制代码

现在我们想添加一个 fullName 属性,该属性值应该为 “Lebron James”。当然,我们不想复制粘贴已有的信息,因此我们可以使用get来实现:

let user = {
  firstName'Lebron',
  lastName'James',
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  },
}
console.log(user.fullName); // Lebron James
复制代码

访问 get 属性 ,不需要通过像函数一样user.fullName()来调用,只需user.fullName就行了,因为背后有 getter 在运行。
同理,我们添加一个 set 属性

let user = {
  firstName'Lebron',
  lastName'James',
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  },
  set fullName(value) {
    [this.firstName, this.lastName] = value.split(" ");
  }
}
user.fullName = 'Steven Jobs'
console.log(user.fullName); // Steven Jobs
复制代码

这样就有了一个”虚拟”的 fullName 属性,并且可读可写

访问器描述符

访问器属性的描述符与数据属性的不同。
对于访问器属性,没有 value 和 writable,但是有 get 和 set 函数。
所以访问器描述符可以有:

  • get  —— 一个没有参数的函数,在读取属性时工作
  • set  —— 带有一个参数的函数,当属性被设置时调用
  • enumerable —— 与数据属性的相同
  • configurable —— 与数据属性的相同

注意:一个属性要么是访问器(具有 get/set 方法),要么是数据属性(具有 value),但不能两者都是。

应用

实现对象的深克隆

通常,当我们克隆一个对象时,我们使用赋值的方式来复制属性,像这样:

let user = {
  name: 'Steven',
  age: 22
}
// 让 name 不可被修改
Object.defineProperty(user, 'name', {
  writable: false
})
let clone = {}
for(let key in user) {
  clone[key] = user[key]
}
// 查看拷贝后的属性描述符
let desc = Object.getOwnPropertyDescriptor(clone, 'name')
console.log(desc);
/*{
  value: 'Steven',
  writable: true, // 这里我们明明改成了false,复制后还是默认为true
  enumerable: true,
  configurable: true
}*/
复制代码

这样并不完美,无法拷贝属性描述符,更完美的拷贝方式如下:

let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(user));
let desc = Object.getOwnPropertyDescriptor(clone, 'name')
console.log(desc);
/*{
  value: 'Steven',
  writable: false,
  enumerable: true,
  configurable: true
}*/
复制代码

可以看到属性描述符也拷贝过来了!

Vue2.0数据响应式实现原理

这里笔者也在学习中,有很多大佬都已经分析过,可以看看他们的文章
当面试官问你Vue响应式原理,你可以这么回答他

当然,还有很多应用场景,等待我们继续学习~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享