面向对象(一):认识对象

213 阅读7分钟

JavaScript 对象系列

面向对象(一):认识对象

面向对象(二):认识JavaScript中对象的原型

面向对象(三):创建多个对象的方案

面向对象(四):掌握原型链

面向对象(五):ES6 类的基本使用

面向对象(六):JavaScript中的7种继承方式

面向对象(七):ES6的class转ES5的源码阅读

第一篇

面向对象是对现实的抽离

对象是JavaScript中一个重要概念,对象可以将多个关联的数据绑定在一起,更好的描述一个事物

  • 描述一个人:Person,具有姓名(name)、年龄(age)、身高(height)、学习(study)等等。
  • 描述一辆车:Car,具有颜色(color)、速度(speed),价格(price)等等。

用对象来描述事物,更加有利于将现实的事物,抽离成代码中的某个数据结构

JavaScript中的面向对象

JavaScript支持多种的编程范式,包括函数式编程面向对象编程

  • JavaScript中的对象被设计成一组属性的无序集合,像一个哈希表,由key和value组成
  • key是一个标识符名称,value是任意类型,也可以是其他对象或则函数类型。
  • 如果value是一个函数,一般被称为对象中的方法

创建对象的两种方式

  1. 构造函数
  2. 字面量
 // 构造函数
 const obj = new Object()
 obj.name = 'copyer'
 obj.age = 18
 // 字面量
 const obj = {
   name: "copyer",
   age: 18,
 }

对象属性的操作

三种基本操作:获取、设置、删除

  1. 获取
 const obj = {
   name: "copyer",
   age: 18,
 }
 obj.name // 获取name属性值
 obj.age  // 获取age属性值
  1. 设置
 const obj = {
   name: "copyer",
   age: 18,
 }
 obj.name = 'james' // 设置obj对象中name属性值
  1. 删除
 const obj = {
   name: "copyer",
   age: 18,
 }
 delete obj.name // 删除obj对象中的name属性
 console.log(obj.name) // undefined

对象的属性描述符

在上面定义了一个对象,对象的属性,可以删除,可以修改,那么怎么才能做到:

  • 不能删除对象中的某个属性;
  • 不能修改对象中的某个属性的属性值;
  • 不能遍历出对象中的某个属性;

想要对对象的某个属性做出比较精确的操作控制,那么就需要使用属性描述符

属性描述符:就是对对象的属性进行精确的控制,比如说:不能让其被修改,不能让其被删除,不能让其被遍历等一些列操作。

1、获取属性描述符

定义一个对象,添加一个属性,都有着默认的属性描述符。怎么知道呢?

Object对象的原型上中提供了两个方法:

Object.getOwnPropertyDescriptor(obj, key): 获取对象的某一个属性的属性描述信息

 const obj = {
     name: 'copyer',
     age; 18
 }
 Object.getOwnPropertyDescriptor(obj, 'name') // 获取name的属性描述
 ​
 /*
 {
   value: 'copyer',
   writable: true,
   enumerable: true,
   configurable: true
 }
 */

Object.getOwnPropertyDescriptors(obj): 获取对象的全部属性的描述信息;

 const obj = {
     name: 'copyer',
     age; 18
 }
 Object.getOwnPropertyDescriptors(obj)  // 加上了一个 s
 /*
 {
   name: {
     value: 'copyer',
     writable: true,
     enumerable: true,
     configurable: true
   },
   age: { 
     value: 18, 
     writable: true, 
     enumerable: true, 
     configurable: true 
   }
 }
 */

2、认识属性描述信息

属性描述信息,是一个对象,对象里面包含四个属性,这四个属性用来精确的控制该属性。

属性作用
value属性值
writable是否可以修改属性值;true:可以修改;false:则不可以被修改;
enumerable是否可以被遍历;true: 可以被遍历;false:不可以被遍历;
configurable是否可以被配置(是否可以被删除,是否可以重新定义属性描述信息) ;true: 可以; false: 不可以;

3、设置属性描述符

上面认识了属性描述信息的内容,那么就可以对里面的内容进行操作了。

Object对象的原型上提供了Object.defineProperty()方法,来对属性进行精确的控制。

3.1、MDN解释

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

 Object.defineProperty(obj, prop, descriptor)

obj: 要定义属性的对象。

prop: 要定义或修改的属性的名称或 Symbol。

descriptor:要定义或修改的属性描述符。

3.2、属性描述符分类

  • 数据属性描述符(成员有: enumerable、configurable、writable、value
  • 存取属性描述符(成员有: enumerable、configurable、get、set

从上面的所属成员就可以看出:writable/value 不能与 get/set共存,但是其实它们的功能都是相似的。

3.3、默认值

默认值分为两种情况:

默认情况一: 针对直接在对象中添加属性的默认值。信息为true

 const obj = {
     name: 'copyer'
 }
 /*
 {
   value: 'copyer',
   writable: true,
   enumerable: true,
   configurable: true
 }
 */

默认情况二: 通过Object.defineProperty()添加的属性

 const obj = {}
 Object.defineProperty(obj, 'name', {}) // 这里传递了一个空对象,内部就会采用默认值
 /*
 {
   value: undefined,
   writable: false,
   enumerable: false,
   configurable: false,
 }
 */

如果是存取数据描述符:getset 是undefined。

3.4、代码演示

3.4.1、修改value值
 const obj = {
   name: "copyer",
 };
 Object.defineProperty(obj, 'name', {
     value: 'james', // 重新设置值为 james
 })
 console.log(name) // james

注意这里修改的现有属性name,采用默认情况一的方式,那么 writable 为 true,所以name修改成功。

 const obj = {}
 Object.defineProperty(obj, 'name', {
     value: 'james',
 })
 obj.name = 'copyer'
 console.log(name) // james

这里name就没有修改成功,是因为,这里采用的默认值情况二的方式,那么 writable 为 false,所以name修改失败。

所以需要区分,采用的默认值的情况。

3.4.2、修改configurable
 const obj = {
   name: "copyer",
 };
 Object.defineProperty(obj, 'name', {
     configurable: false
 })
 delete obj.name
 console.log(name) // copyer

说明obj对象的name属性没有删除成功

 const obj = {};
 Object.defineProperty(obj, "name", {
   value: "copyer",
   configurable: false,
 });
 Object.defineProperty(obj, "name", {
   value: "james",
 });
 console.log(obj.name); 
 // 直接报错
3.4.3、修改enumerable
 const obj = {
   name: 'copyer',
   age: 18
 }
 console.log(Object.keys(obj));  // ["name", "age"]
 ​
 // 修改之后
 Object.defineProperty(obj, 'age', {
   enumerable: false
 })
 console.log(Object.keys(obj));  // ["name"]
3.4.4、修改writable
 const obj = {
   name: 'copyer',
   age: 18
 }
 obj.age = 100
 console.log(obj.age);  // 100
 // 修改之后
 Object.defineProperty(obj, 'age', {
   writable: false
 })
 obj.age = 100
 console.log(obj.age);  // 18

3.5、 同时修改多个属性描述符

Object原型上提供了 defineProperties()方法。

 const obj = {};
 Object.defineProperties(obj, {
   name: {
     value: "copyer",
     configurable: true,
     writable: false,
     enumerable: true,
   },
   age: {
     value: 17,
     configurable: true,
     writable: false,
     enumerable: true,
   },
 });
 console.log(obj); // { name: 'copyer', age: 17 }

3.6、认识set/get

存取属性描述符中是没有value和writable属性的,代替它们的就是get 和 set。

 const obj = {
   _name: 'copyer'
 };
 Object.defineProperty(obj, "name", {
   configurable: true,
   enumerable: true,
   set: function (value) {
     console.log('进入了set方法')
     this._name = value;
   },
   get: function () {
     console.log('进入了get方法')
     return this._name;
   },
 });
 obj.name = 'james' // 调用set方法
 console.log(obj.name); // 调用了get方法
 ​
 // 打印结果:
 // 进入了set方法
 // 进入了get方法
 // james

使用场景:

  1. 隐藏某一个私有属性被希望直接被外界使用和赋值
  2. 如果我们希望截获某一个属性它访问和设置值的过程时, 也会使用存储属性描述符(Vue2的响应式源码的实现方式)

3.7 小知识点的补充

var obj = {
  name: 'copyer',
  age: 15
}

Object.defineProperty(obj, 'height', {
  value: 1.88,
  // 三个配置属性默认都是false
  // enumerable: false,
  // writable: false,
  // configurable: false,
})

console.log(obj)  // {name: 'copyer', age: 15}

上面这段代码没有任何问题吧,因为 height 属性是不可以被枚举的,所以不能被遍历出来。 但是呢?如果放到浏览器上,有一个奇怪的现象:

09_1.png 这是浏览器上打印的结果,可以打印出height属性,但是呢,它的属性值的颜色要浅一点,这是浏览器提供的。打印不就是为了调试嘛,浏览器就想让你看清整个对象,只不过用颜色来标记一下,有些属性的属性描述符是不一样的。

对象方法

Object.preventExtensions(): 阻止对象继续添加新的属性

 const obj = {
   name: 'copyer',
   age: 18
 }
 Object.preventExtensions(obj)
 obj.height = 188
 ​
 console.log(obj) // { name: 'copyer', age: 18 }

Object.seal(): 禁止对象删除里面属性和重新配置,就相当于设置属性描述信息:{configurable: false}

 const obj = {
   name: "copyer",
   age: 18,
 };
 // 方式一: 自己实现,手动设置
 for (let key in obj) {
   Object.defineProperty(obj, key, {
     configurable: false,
     enumerable: true,
     writable: true,
     value: obj[key],
   });
 }
 // 方式二:
 Object.seal(obj)

Object.freeze():冻结一个对象,让对象属性不可修改。

MDN解释

实际上是调用了 Object.seal()方法,然后再加上 {writable: false}

 const obj = {
   name: "copyer",
   age: 18,
 };
 // 方式一: 自己实现,手动设置
 for (let key in obj) {
   Object.defineProperty(obj, key, {
     configurable: false,
     enumerable: true,
     writable: false,
     value: obj[key],
   });
 }
 // 方式二:
 Object.freeze(obj)