组件库项目中运用的原型模式 | 青训营笔记

97 阅读4分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 6 天

1. 主要内容

概念介绍,解决的问题

代码演示和 UML 类图

应用场景 + 原型和原型链 + 对象属性描述符

1)学习方法

UML类图要结合代码理解

设计模式要结合使用场景,否则记不住

原型链的图,自己亲自画一遍

2)注意事项

原型模式不常用,但原型链是 JS 基础,必须掌握 属性描述符日常不会直接使用,但它是理解对象属性的重要基础

2. 原型模式 - 介绍和演示

image-20230111100307622 image-20230111100326031

3. 原型和原型链

1)原型

函数(class)都有显示原型 prototype

对象都有隐式原型 image-20230111100508956

对象 image-20230111100508956 指向其构造函数的 prototype

2)class 语法糖

class 是 function 的语法糖(原型)

 class Foo {
   name: string
   age: number
   constructor(name:string, age:number){
     this.name = name
     this.age = age
   }
   getName(){
     return this.name
   }
 }
 const f2 = new Foo('李四', 21)
 ​
 console.log(Foo.prototype)
 console.log(f2.__proto__)
 console.log(f2.__proto__ === Foo.prototype)   // true

3)原型链

通过 new 创建出来的实例对象,全部都可以通过 image-20230111100508956 获取到构造函数

而构造函数则可以通过 prototype 来获取它的构造函数

image-20230111103717362

4)继承

image-20230111103910754

5)总结

函数(class)都有显示原型 prototype

对象都有隐式原型 image-20230111100508956,指向构造函数的 prototype

原型链(图)和继承

4. 原型模式 - 场景

Objext.create() 使用方法:

 const obj1 = Object.create({})
 // obj1.__proto__ 的隐式原型指向的就是 {}
 ​
 function sayHi(){
   console.log('Hi')
 }
 const obj2 = Object.create(sayHi)
 // obj2.__proto__ 的隐式原型指向的就是 ƒ sayHi(){} 
 // obj2.__proto__() 获得 Hi

1)面试题

{} 和 Object.create({}) 有什么区别?

{ } 使用字面量的方式创建了一个对象,它的隐式原型指向的是 Object 的 prototype

Object.create({}) 将创建的对象的 隐式原型 指向成为 { }

5. 对象属性描述符

获取和设置

有哪些属性描述符

原型属性的描述符

对象属性描述符的 ts 类型:PropertyDescriptor

1)代码演示 - 获取和设置

const obj = { x : 100 }

获取属性描述符状态 API :Object.getOwnPropertyDescriptor(obj, 'x')

image-20230111110544473

  • configurable 可配置
  • enumerable 可枚举(通过 for in)
  • value 当前值
  • writable 可编辑可重写

设置属性(并配置属性描述符):Object.defineProperty(obj, 'y', {

value: 200,

writable: false // 配置了,重新赋值属性则不可操作

})

image-20230111110956110

Object.defineProperty - Vue2 数据响应式 核心 API

2)有哪些属性描述符

value

configurable

writable

enumerable

A. value

定义属性值

没有 value,则看不到对象属性

但可以通过 get set 来操作属性值


演示代码:

image-20230111111432834

image-20230111111451584

B. configurable

是否可以 delete 删除,并重新定义

是否可以修改其他属性描述符

是否可以修改 get set

设置为 true,以上都可以修改;设置为 false 以上都不可以使用

image-20230111112004239

C. writable

属性值是否可以被修改

对比 Object.freeze() "冻结"

  1. 现有属性不可以被修改
  2. 不可以添加新属性
 // Object.freeze 冻结:1. 现有属性不可以被修改  2. 不可添加新属性
 const obj = { x: 100, y: 200 }
 Object.freeze(obj)
 // obj.x = 101   // 不成功,js 层面抛错
 // console.log(obj)
 console.log(Object.getOwnPropertyDescriptor(obj, 'x'))  // { configurable: false, writable: false }
 ​
 // obj.z = 300   // 不成功,js 层面抛错
 // console.log(obj) 

Object.isFrozen(obj) 检测是否冻结的对象

对比 Object.seal() "密封"

  1. 现有属性可以被修改
  2. 不可以添加新的属性
 // Object.seal 密封:1.现有属性可以被修改 2.不可以添加新属性
 const obj1 = { x: 100, y: 200 }
 Object.seal(obj1)
 obj1.x = 101
 console.log(obj1.x)
 console.log(Object.getOwnPropertyDescriptor(obj1, 'x'))  // { configurable: false }
 // obj1.z = 300   // 不成功,js 层面抛错
 console.log(Object.isSealed(obj1))  

Object.isSealed(obj) 检测是否密封的对象

注意:如果你的 Vue2 的 data 不需要响应式可以通过,Object.freeze Object.seal 来将将其变为非响应式

D. enumerable

是否可以通过 for...in 遍历

对比 for...of(迭代器模式)

 const obj = { x: 100 }
 Object.defineProperty(obj, 'y', {
   value: 200,
   enumerable: false
 })
 Object.defineProperty(obj, 'z', {
   value: 300,
   enumerable: true
 })
 ​
 for (let key in obj) {
   console.log(key)  // x z
 }
 ​
 'x' in obj  // true
 'y' in obj  // true
 'z' in obj  // true

E. 原型属性的 enumerable

多年之前,for...in 可以遍历出原型属性

当时需要 hasOwnProperty 来判断:是否是原型属性

现在 for...in 通过 enumerable 来判断

image-20230111173757036

3)总结

获取和设置属性描述符

各个属性描述符的作用

尤其注意:enumerable 和 for...in(原型属性)

4)扩展:如何遍历对象身上的 Symbol 属性

使用 Reflect.ownKeys() 获取指定对象身上的,所有属性包含(Symbol)

使用 Object.getOwnPropertySymbols() 获取指定对象身上的,所有 Symbol 属性

使用 Object.getOwnPropertyNames() 获取指定对象身上的属性,不包含(Symbol)属性

image-20230111175812610

6. 总结

1)内容回顾

概念介绍,解决的问题

代码演示和 UML 类图

应用场景 + 原型和原型链

2)重要细节

原型和原型链(图)

enumerable 和 for...in

for...in 对比 for...of

3)注意事项

原型模式不常用,但原型链是JS基础,必须掌握

属性描述符日常不会直接使用,但它是理解对象属性的重要基础