JavaScript设计模式
2021-02-28 21:23:32 427 举报
AI智能生成
前端 设计模式
作者其他创作
大纲/内容
前置知识
面向对象的一些概念
public
可以自由的访问程序里定义的成员
在TypeScript里,成员都默认为 public
在TypeScript里,成员都默认为 public
private
不能在声明它的类的外部访问
protected
不能在声明它的类的外部访问
但是 protected成员在派生类中仍然可以访问
但是 protected成员在派生类中仍然可以访问
static
属性存在于类本身上面而不是类的实例上
如同在实例属性上使用 this. 前缀来访问属性一样,这里我们使用 类名. 来访问静态属性
如同在实例属性上使用 this. 前缀来访问属性一样,这里我们使用 类名. 来访问静态属性
abstract
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化
abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法
抽象类中的抽象方法不包含具体实现并且必须在派生类中实现
constructor
面向对象编程和面向对象设计的五个基本原则
单一功能原则
开放封闭原则
里式替换原则
接口隔离原则
依赖反转原则
创建型
构造器模式
作用
创建对象
抽象了每个对象实例的变与不变,不变得字段名称,变得是字段的值
抽象了每个对象实例的变与不变,不变得字段名称,变得是字段的值
如何使用
构造函数
工厂模式
是什么
工厂模式其实就是将创建对象的过程单独封装
作用
抽象不同构造函数(类)之间的变与不变
工厂模式的目的,就是为了实现无脑传参
工厂模式的目的,就是为了实现无脑传参
简单工厂
如何使用
声明一个函数及其N个参数,在函数内部判断不同的参数,从而生成不同的对象
抽象工厂
层级
抽象工厂(抽象类,它不能被用于生成具体实例)
具体工厂(用于生成产品族里的一个具体的产品)
抽象产品(抽象类,它不能被用于生成具体实例)
具体产品(用于生成产品族里的一个具体的产品所依赖的更细粒度的产品)
单例模式
含义
保证一个类仅有一个实例,并提供一个访问它的全局访问点
作用
不管创建多少次,它都只返回第一次所创建的那唯一的一个实例
如何做
判断自己是否已经创建过一个实例
静态方法版本
闭包版本
面试题
实现一个 Storage
实现一个全局的模态框
原型模式
是什么
只要我们还在借助Prototype来实现对象的创建和原型的继承,那么我们就是在应用原型模式
为什么使用
原型模式不仅是一种设计模式,
还是一种编程范式(programming paradigm),
是 JavaScript 面向对象系统实现的根基
还是一种编程范式(programming paradigm),
是 JavaScript 面向对象系统实现的根基
组成
原型
构造函数上存在 prototype 属性
指向
原型对象
实例上存在 __proto__属性
指向
原型对象
原型上存在 constructor 属性
指向
构造函数
原型链
面试题
JS中的深拷贝
JSON.stringify
无法处理 function
无法处理正则
........
递归 & data.hasOwnProperty(key)
逻辑
调用深拷贝方法,若属性为值类型,则直接返回;若属性为引用类型,则递归遍历
除了考虑 Array、Object,还需要考虑一些其它的数据结构(Map、Set 等);此外还有一些极端 case(循环引用等)
结构型
装饰器模式
是什么
在不改变原对象的基础上,通过对其进行包装拓展,使原有对象可以满足用户的更复杂需求
不想去关心它已有的业务逻辑是啥样的
对它已有的功能做个拓展,只关心拓展出来的那部分新功能如何实现
如何使用
@语法糖
@语法糖只能修饰类或者类的方法
如果需要修饰函数,可以使用高阶函数的方式
如果需要修饰函数,可以使用高阶函数的方式
代码中可以干什么
装饰类
原理: 给类自身添加属性
装饰类里面的方法
原理: 给类的原型对象添加属性
装饰函数
原理:高阶函数
场景
React中的HOC
Redux connect函数
调用 connect 方法来把状态和组件绑在一起
适配器模式
含义
把一个类的接口变换成客户端所期待的另一种接口,以解决不兼容的问题
优点
暴露给用户的都是十分简单的统一的东西——统一的接口,统一的入参,统一的出参,统一的规则
如何做
不仅调用的接口名是同一个,连入参、出参的格式都只需要掌握同一套
场景
http 适配器
xhr 适配器
fetch的封装
ajax封装
axios封装
代理模式
what
在某些情况下,出于种种考虑/限制,一个对象不能直接访问另一个对象,需要一个第三者(代理)牵线搭桥从而间接达到访问目的,这样的模式就是代理模式
why
既可以是为了加强控制、拓展功能、提高性能,
也可以仅仅是为了优化我们的代码结构、实现功能的解耦。
无论是出于什么目的,这种模式的套路就只有一个—— A 不能直接访问 B,A 需要借助一个帮手来访问 B,这个帮手就是代理器
也可以仅仅是为了优化我们的代码结构、实现功能的解耦。
无论是出于什么目的,这种模式的套路就只有一个—— A 不能直接访问 B,A 需要借助一个帮手来访问 B,这个帮手就是代理器
真实例子
VPN
ES6中的Proxy
前端场景
事件代理
案例: 考虑到事件本身具有“冒泡”的特性,当我们点击 a 元素时,点击事件会“冒泡”到父元素 div 上,从而被监听到。如此一来,点击事件的监听函数只需要在 div 元素上被绑定一次即可,而不需要在子元素上被绑定 N 次——这种做法就是事件代理,它可以很大程度上提高我们代码的性能
虚拟代理
案例: 实现前端图片预加载
明确几个概念
图片懒加载
它是针对图片加载时机的优化:在一些图片量比较大的网站,比如电商网站首页,或者团购网站、小游戏首页等。
如果我们尝试在用户打开页面的时候,就把所有的图片资源加载完毕,那么很可能会造成白屏、卡顿等现象
此时我们会采取“先占位、后加载”的方式来展示图片 —— 在元素露出之前,我们给它一个 div 作占位,当它滚动到可视区域内时,再即时地去加载真实的图片资源,这样做既减轻了性能压力、又保住了用户体验。
如果我们尝试在用户打开页面的时候,就把所有的图片资源加载完毕,那么很可能会造成白屏、卡顿等现象
此时我们会采取“先占位、后加载”的方式来展示图片 —— 在元素露出之前,我们给它一个 div 作占位,当它滚动到可视区域内时,再即时地去加载真实的图片资源,这样做既减轻了性能压力、又保住了用户体验。
图片懒加载 先于图片预加载
图片预加载
预加载主要是为了避免网络不好、或者图片太大时,页面长时间给用户留白的尴尬。
常见的操作是先让这个 img 标签展示一个占位图,然后创建一个 Image 实例,
让这个 Image 实例的 src 指向真实的目标图片地址、观察该 Image 实例的加载情况 —— 当其对应的真实图片加载完毕后,即已经有了该图片的缓存内容,再将 DOM 上的 img 元素的 src 指向真实的目标图片地址。
此时我们直接去取了目标图片的缓存,所以展示速度会非常快,从占位图到目标图片的时间差会非常小、小到用户注意不到,这样体验就会非常好了
常见的操作是先让这个 img 标签展示一个占位图,然后创建一个 Image 实例,
让这个 Image 实例的 src 指向真实的目标图片地址、观察该 Image 实例的加载情况 —— 当其对应的真实图片加载完毕后,即已经有了该图片的缓存内容,再将 DOM 上的 img 元素的 src 指向真实的目标图片地址。
此时我们直接去取了目标图片的缓存,所以展示速度会非常快,从占位图到目标图片的时间差会非常小、小到用户注意不到,这样体验就会非常好了
缓存代理
案例: 对传入的参数进行求和--通过闭包存储已计算的值,每次计算前就进行比较
保护代理
在访问层面做文章,在 getter 和 setter 函数里去进行校验和拦截,确保一部分变量是安全/无法访问的
Proxy,就是为拦截而生的,所以我们目前实现保护代理时,考虑的首要方案就是 ES6 中的 Proxy
行为模型
策略模式
what
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
算法提取
某一个功能函数的逻辑
算法封装
把某一功能点对应的逻辑给提出来
分发优化
场景
将if else或switch 改写为对象映射的方式
状态模式
观察者模式
what
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新
why
是所有 JavaScript 设计模式中使用频率最高,面试频率也最高的设计模式
这种模式不仅在业务开发中遍地开花,在日常生活中也是非常常见的
即便是两个分离的、毫不相关的模块,也可以实现数据通信
组成
发布者
增加订阅者
通知订阅者
移除订阅者
订阅者
被通知
去执行
面试题
Vue数据双向绑定(响应式系统)的实现原理
实现一个Event Bus/ Event Emitter (发布-订阅模式)
与发布 - 订阅模式的比较
本质
没有区别
区别
在于是否存在第三方、发布者能否直接感知订阅者
发布 - 订阅模式的
含义
发布者不直接触及到订阅者、而是由统一的第三方来完成实际的通信的操作,叫做发布-订阅模式
例子
老王把需求文档上传到了公司统一的需求平台上,需求平台感知到文件的变化、自动通知了每一位订阅了该文件的开发者
发布者完全不用感知订阅者,不用关心它怎么实现回调方法,事件的注册和触发都发生在独立于双方的第三方平台(事件总线)上。发布-订阅模式下,实现了完全地解耦。
观察者模式
含义
发布者直接触及到订阅者的操作,叫观察者模式
例子
老王把所有的开发者拉了一个群,直接把需求文档丢给每一位群成员
并没有完全地解决耦合问题——被观察者必须去维护一套观察者的集合,这些观察者必须实现统一的方法供被观察者调用
迭代器模式
what
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示
它就解决这一个问题——遍历
ES6约定,任何数据结构只要具备Symbol.iterator属性(这个属性就是Iterator的具体实现,它本质上是当前数据结构默认的迭代器生成函数),就可以被遍历——准确地说,是被for...of...循环和迭代器的next方法遍历。 事实上,for...of...的背后正是对next方法的反复调用。
在ES6中,针对Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象这些原生的数据结构都可以通过for...of...进行遍历
在ES6中,针对Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象这些原生的数据结构都可以通过for...of...进行遍历
场景
遍历类数组
例子: 循环`const aNodes = document.getElementsByTagName('a')`
案例
ES6 迭代器(Iterator)
面试题
实现一个迭代器生成函数
ES6方式
ES5方式
0 条评论
下一页
为你推荐
查看更多