原生 js 中没有设计模式,它是模仿 java 语言中的设计模式来实现。
设计模式分为三种类型,共23种。
工厂模式就是做一个对象创建的封装,并将创建的对象 return 出去。
工厂模式适用于:
工厂模式普通写法:
function CreateMan(name,age) {
var obj={};
obj.name=name;
obj.age=age;
obj.sayName=function () {
return this.name;
};
return obj;
}
var p1=CreateMan("zhangsan",20);
var p2=CreateMan("wangwu",30);
工厂模式复杂写法,每一个类都应该是一个单独的文件:
class CarFactory{
static get CAR(){
return "Car";
}
static get BUS(){
return "Bus";
}
static get TAXI(){
return "TAXI";
}
static createCar(type){
switch(type){
case CarFactory.CAR:
return new Car();
case CarFactory.BUS:
return new Bus();
case CarFactory.TAXI:
return new Taxi();
}
}
}
class Car{
constructor(){
}
getName(){
console.log("这是一个:"+this.constructor.name)
}
}
class Bus extends Car{
constructor(){
super();
}
}
class Taxi extends Car{
constructor(){
super();
}
}
var car1=CarFactory.createCar(CarFactory.BUS);
var car2=CarFactory.createCar(CarFactory.TAXI);
car1.getName();//这是一个:Bus
car2.getName();//这是一个:Taxi
单例模式就是运行完成后返回的对象是唯一的,如果已经创建出来就直接返回。
单例模式的普通写法:
var obj;
function getInstance(){
return obj || (obj={});
}
var s=getInstance();
var o=getInstance();
console.log(s===o);//true
单例模式的复杂写法:
//使用闭包的形式,防止在外部更改 _instance 的值
let ViewModel=(function(){
var _instance;
return class ViewModel{
constructor(){
}
static getInstance(){
if(!_instance) _instance=new ViewModel();
return _instance;
}
}
})();
var a=ViewModel.getInstance();
var b=ViewModel.getInstance();
console.log(a===b);//true
构造器是一个当新建对象的内存被分配后,用来初始化该对象的一个特殊函数。对象构造器是被用来创建特殊类型的对象的,首先它要准备使用的对象,其次在对象初次被创建时,通过接收参数,构造器要用来对成员的属性和方法进行赋值。
function CreateMan(name,age) {
this.name=name;
this.age=age;
this.toString=function () {
return this.name+"年龄:"+this.age;
}
}
var p1=new CreateMan("zhangsan",20);
var p2=new CreateMan("wangwu",30);
console.log(p1.toString());
console.log(p2.toString());
桥接模式可以理解为将抽象部分与它的实现部分分离,使它们都可以独立地变化。它的本质就是函数的封装,在实现api的时候,桥接模式非常常用。例如 some() 的实现过程,some函数并不关心 fn 里面的具体实现,fn 里面的逻辑也不会被 some 函数的改写影响。
var list = [1,2,3,4,5];
function some(arr,fn){
for(var i=0;i<arr.length;i++){
//如果数组的元素是空,则跳过
if(arr[i]===undefined) continue;
//如果fn()的返回结果是true,则返回true
if(fn(arr[i],i,arr)) return true;
}
//默认返回false
return false;
}
var s = some(list,function(item,index,arr){
return item>4
})
console.log(s);//true
外观模式是一种无处不在的模式,外观模式提供一个高层接口,这个接口使得客户端或者子系统调用起来更加方法。比如:这样就方便组装,如果一开始就把两个写到一个函数中,那就不能够只单独调用其中一个了。
function getName() {
return "xiaoming";
}
function getSex() {
return "man";
}
function getUserInfo() {
var info=getName()+getSex();
return info;
}
console.log(getUserInfo());
享元模式是一种用于性能优化的模式,它的目标是尽量减少共享对象的数量。通俗的讲,享元模式就是用来减少程序所需的对象个数。
举一个例子,网页中的瀑布流,或者webqq的好友列表中,每次往下拉时,都会创建新的div。那么如果有很多div呢?浏览器岂不是卡死了?所以我们会想到一种办法,就是把已经消失在视线外的div都删除掉,这样页面就可以保持一定数量的节点,但是频繁的删除和添加节点,又会带来很大的性能开销。这个时候就可以用到享元模式了,享元模式可以提供一些共享的对象以便重复利用。比如页面中只能显示10个div,那始终出现在用户视线中的这10个div就可以写成享元。原理其实很简单,把刚隐藏起来的div放到一个数组中,当需要div的时候,先从该数组中取,如果数组中已经没有了,再重新创建一个。这个数组里的div就是享元,它们每一个都可以当作任何用户信息的载体。
当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一些额外的事情。
代理模式主要有三种:
// 主体,发送消息
function sendMsg(msg) {
console.log(msg);
}
// 代理,对消息进行过滤
function proxySendMsg(msg) {
// 无消息则直接返回,这是保护代理
if (typeof msg === "undefined") {
return;
}
// 有消息则进行过滤,这是虚拟代理
msg = ("" + msg).replace(/小朋友/g, '');
sendMsg(msg);
}
sendMsg('你好小朋友'); //你好小朋友
proxySendMsg('你好小朋友'); //你好
proxySendMsg();
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态时,所有依赖于它的对象都会得到通知并被自动更新。通俗点理解,就是面试官是被观察者,而等待通知的人是观察者。
观察者模式的三个步骤:添加至观察列表,从观察列表中移除,进行观察。
javascript中平时接触的dom事件,其实就是一种观察者模式的体现:
div.onclick=function click(){ console.log("click" ) }
观察者模式实现弹幕效果:
html结构代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<style>
.view{
width: 536px;
height: 480px;
border: 1px solid #000000;
overflow: hidden;
position: relative;
}
input{
width: 450px;
border:1px solid #CCCCCC;
height: 30px;
}
button
{
width:86px;
height: 35px;
}
style>
head>
<body>
<div class="view">
<video src="./video/test3.mp4" controls>video>
div>
<input type="text"><button>发送button>
<script type="module">
import Bubble from "./js/observer/Bubble.js";
var txt;
init();
function init(){
var bn=document.querySelector("button");
txt=document.querySelector("input");
bn.addEventListener("click",clickHandler);
window.addEventListener("keyup",clickHandler);
}
function clickHandler(e){
if(e.type==="keyup" && e.keyCode!==13) return;
if(txt.value.trim().length===0) return;
//新建弹幕
var p=new Bubble(txt.value);
p.appendTo(".view");
txt.value="";
}
script>
body>
html>
Bubble.js文件:
import TimeManager from "./TimeManager.js";
import IMove from "./IMove.js";
export default class Bubble extends IMove{
left=0;//弹幕定位的left值
speed=3;//弹幕移动的速度
w=0;//弹幕父容器的宽度
constructor(msg){
//执行超类的构造函数
super();
//创建弹幕
this.elem=this.createElem(msg);
}
createElem(msg){
//单例模式
if(this.elem) return this.elem;
//创建弹幕
var div=document.createElement("div");
div.textContent=msg;
Object.assign(div.style,{
position:"absolute",
whiteSpace:"nowrap"
});
return div;
}
appendTo(parent){
//添加到父容器中
if(HTMLElement.isPrototypeOf(parent.constructor)){
parent.appendChild(this.elem);
}else if(parent.constructor===String){
parent=document.querySelector(parent);
if(!parent) return;
parent.appendChild(this.elem);
}
//获取父容器的一些值
var rect=parent.getBoundingClientRect();
this.left=rect.width+10
Object.assign(this.elem.style,{
left:this.left+"px",
top:Math.random()*rect.height/2+"px"
})
this.w=this.elem.offsetWidth;
//添加到观察列表
TimeManager.add(this);
}
update(){
//执行移动函数
this.left-=this.speed;
this.elem.style.left=this.left+"px";
if(this.left<-this.w){
//删除元素并从观察列表移除
this.elem.remove();
TimeManager.remove(this);
}
}
}
TimeManager.js代码:
import IMove from "./IMove.js";
export default class TimeManager{
static list=[];
static ids;
constructor(){
}
//添加到观察列表
static add(elem){
//如果已经添加,则直接跳出
if(TimeManager.list.indexOf(elem)>-1) return;
TimeManager.list.push(elem);
//如果观察列表中有内容并且TimeManager.ids为false时,设置动画
if(TimeManager.list.length>0 && !TimeManager.ids){
TimeManager.ids=setInterval(TimeManager.animation,16);
}
}
//从观察列表中移除
static remove(elem){
var index=TimeManager.list.indexOf(elem);
if(index<0) return;
//先将元素设为null,再移除
TimeManager.list[index]=null;
TimeManager.list.splice(index,1);
//如果观察列表中没有内容时,则清除定时器,并将TimeManager.ids设为0
if(TimeManager.list.length===0){
clearInterval(TimeManager.ids);
TimeManager.ids=0;
}
}
//执行动画
static animation(){
for(var i=0;i<TimeManager.list.length;i++){
//IMove在这里类似于是一个接口,用来判断当前对象中是不是有update方法
if(IMove.isPrototypeOf(TimeManager.list[i].constructor)) TimeManager.list[i].update();
}
}
}
IMove.js文件,在这里相当是一个接口,用来确定当前new Bubble() 当中是不是有update方法。
export default class IMove{
constructor(){
}
update(){
}
}