这是我参与「第五届青训营」伴学笔记创作活动的第 6 天
1. 主要内容
概念介绍,解决的问题
代码演示和 UML 类图
应用场景 + 原型和原型链 + 对象属性描述符
1)学习方法
UML类图要结合代码理解
设计模式要结合使用场景,否则记不住
原型链的图,自己亲自画一遍
2)注意事项
原型模式不常用,但原型链是 JS 基础,必须掌握 属性描述符日常不会直接使用,但它是理解对象属性的重要基础
2. 原型模式 - 介绍和演示
3. 原型和原型链
1)原型
函数(class)都有显示原型 prototype
对象都有隐式原型
对象 指向其构造函数的 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 创建出来的实例对象,全部都可以通过 获取到构造函数
而构造函数则可以通过 prototype 来获取它的构造函数
4)继承
5)总结
函数(class)都有显示原型 prototype
对象都有隐式原型 ,指向构造函数的 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')
- configurable 可配置
- enumerable 可枚举(通过 for in)
- value 当前值
- writable 可编辑可重写
设置属性(并配置属性描述符):Object.defineProperty(obj, 'y', {
value: 200,
writable: false // 配置了,重新赋值属性则不可操作
})
Object.defineProperty - Vue2 数据响应式 核心 API
2)有哪些属性描述符
value
configurable
writable
enumerable
A. value
定义属性值
没有 value,则看不到对象属性
但可以通过 get set 来操作属性值
演示代码:
B. configurable
是否可以 delete 删除,并重新定义
是否可以修改其他属性描述符
是否可以修改 get set
设置为 true,以上都可以修改;设置为 false 以上都不可以使用
C. writable
属性值是否可以被修改
对比 Object.freeze() "冻结"
- 现有属性不可以被修改
- 不可以添加新属性
// 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() "密封"
- 现有属性可以被修改
- 不可以添加新的属性
// 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 来判断
3)总结
获取和设置属性描述符
各个属性描述符的作用
尤其注意:enumerable 和 for...in(原型属性)
4)扩展:如何遍历对象身上的 Symbol 属性
使用 Reflect.ownKeys() 获取指定对象身上的,所有属性包含(Symbol)
使用 Object.getOwnPropertySymbols() 获取指定对象身上的,所有 Symbol 属性
使用 Object.getOwnPropertyNames() 获取指定对象身上的属性,不包含(Symbol)属性
6. 总结
1)内容回顾
概念介绍,解决的问题
代码演示和 UML 类图
应用场景 + 原型和原型链
2)重要细节
原型和原型链(图)
enumerable 和 for...in
for...in 对比 for...of
3)注意事项
原型模式不常用,但原型链是JS基础,必须掌握
属性描述符日常不会直接使用,但它是理解对象属性的重要基础