为什么别人写得代码又好看又好用?
有人说从“写代码”到“写好代码”到“设计代码”,不仅是技术的提升,更是编程思维的提升。现在想想很道理,记得刚毕业时,写代码就单纯为了实现功能,虽然有时也会模仿别人结构,但是对功能结构了解得很浅,渐渐地开始注重代码的复用性和可读性,重新回顾一下设计模式,才看懂了别人用的结构,另外面试也会出手写设计模式的题目哦~
概念
实例
单例模式
思路:在构造函数返回对象之前,判断是否已经实例化,使其只在内存中创建一次
- new.target 或者 instanceof
1)在类的构造方法中,new.target指向构造方法类名
2) instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。 语法 object instanceof constructor
class User {
constructor() {
if (new.target !== User) return;
// if (!(this instanceof User)) return;
if (!User._instance) {
this.name = 'hh';
User._instance = this
}
return User._instance
}
}
// function User() {
// if (!(this instanceof User))return
// if (!User._instance) {
// this.name = '无名'
// User._instance = this//当前函数的实例
// }
// return User._instance
// }
const u1 = new User()
const u2 = new User()
console.log(u1===u2);// true
- static静态方法(类方法,访问方式:类名.方法名)
class User {
constructor() {
this.name = 'hh'
}
static getInstance() {
if (!User._instance) User._instance = this
return User._instance
}
}
const u1 = User.getInstance()
const u2 = User.getInstance()
console.log(u1 === u2);// true
- 闭包
function User() {
this.name = '无名'
}
User.getInstance = (function () {
var instance
return function () {
if (!instance) {
instance = new User()
}
return instance
}//闭包
})()
const u1 = User.getInstance()
const u2 = User.getInstance()
console.log(u1 === u2);// true
原型模式
思路:在原型链上扩展方法,使其子代得以继承
- prototype
const Parents = {
name: '姚',
life: function () {
console.log(this.name + '要工作学习');
}
}
function createKid(name) {
function kid() { };
kid.prototype = Parents;
let kid1 = new kid();
kid1.name = kid1.name + name;
return kid1
}
let kid1 = createKid('小孩');
kid1.life(); //姚小孩要工作学习
工厂模式 1
思路:把产品属性和实例化过程封装到工厂里,通过传参就可以获取对象
class Factory {
constructor(opt) {
this._type = opt.type; //产品名称
}
static createProduct(param) {
//静态方法:直接通过类来调用,不会被实例继承,this指的是类不是实例对象
switch (param) {
case 'product1':
return new Factory({ type: '产品1' });
case 'product2':
return new Factory({ type: '产品2' });
default:
throw new Error('参数错误, 可选参数:product1、product2')
}
}
getProductType() {
return this._type;
}
}
let p1 = Factory.createProduct('product1');
console.log(p1.getProductType());
let p2 = Factory.createProduct('product2');
console.log(p2.getProductType());
用户权限设置就可以使用简单工厂模式,见参考 1
注意:使用this.$router.addRoutes方法添加的路由刷新后不能保存,需要本地保存用户信息在页面刷新时重新加载路由
装饰模式
思路:封装一个方法包含新的和旧的逻辑
var oldCloth = function () {
console.log('我是一堆你看不懂的老逻辑');
}
var newCloth = function () {
console.log('我是新的逻辑');
}
wear = function () {
_oldCloth();
newCloth();
}
wear();
代理模式2
思路:就是在原功能基础上扩充,使用代理访问原功能和扩充功能
- 图片预加载
var myImage = (function () {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function (src) {
imgNode.src = src
}
}
})();
var proxyImage = (function () {
var img = new Image()
img.onload = function () {
myImage.setSrc(img.src) //当img.src加载完成执行这个操作
}
return {
setSrc: function (src) {
img.src = src
myImage.setSrc('/loading.gif') //预加载loading图片
}
}
})();
proxyImage.setSrc('/real.png');
var mult = function () {
console.log('开始计算乘积');
var a = 1;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return a;
};
var proxyMult = (function () {
var cache = {}
return function () {
// arguments是对象[Arguments] { '0': 2, '1': 3 }
let id = Array.prototype.join.call(arguments, ',')
console.log(typeof id);//string
console.log(id);//"2,3"
if (cache[id]) {
return cache[id]
} else {
return cache[id] = mult.apply(this, arguments) //缓存中记录{"2,3":6}
}
}
})()
proxyMult(2, 3); // 输出:6
- 基于VUE3的Proxy
//明星
let star = {
name: '张XX',
age: 25,
phone: '13910733521'
}
// 经纪人
let agent = new Proxy(star, {
get: function (target, key) {
if (key === 'phone') {
// 返回经纪人自己的手机号
return '18611112222'
}
if (key === 'price') {
// 明星不报价,经纪人报价
return 120000
}
return target[key]
},
set: function (target, key, val) {
if (key === 'customPrice') {
if (val < 100000) {
// 最低 10w
throw new Error('价格太低')
} else {
target[key] = val
return true
}
}
}
})
// 主办方
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)
// 想自己提供报价(砍价,或者高价争抢)
agent.customPrice = 150000
// agent.customPrice = 90000 // 报错:价格太低
console.log('customPrice', agent.customPrice)
发布订阅模式
思路:明确角色(发布者、订阅者)–> 订阅方法(存储订阅者事件,用于之后消息的接收) --> 触发方法(遍历订阅者存储的回调函数)
- 观察者模式(区别:订阅者和发布者不关联)
class Subject {
constructor() {
this.subs = [];
}
addSub(sub) {//订阅
this.subs.push(sub);
}
notify() {//触发
this.subs.forEach(sub => {
sub.update(); //目标和观察的联系
});
}
}
class Observer {
update() {
console.log('update');
}
}
let subject = new Subject();
let ob = new Observer();
//目标添加观察者了
subject.addSub(ob);
//目标发布消息调用观察者的更新方法了
subject.notify(); //update
- 发布订阅模式(有个事件通道也叫调度中心)
class PubSubEvent {
constructor() {
this._listenerStore = {};//事件存储
}
onMessage(key, fn) { //订阅事件,fn回调函数为事件通道
if (this._listenerStore[key]) {
this._listenerStore[key].push(fn);
} else {
this._listenerStore[key] = [fn];
}
}
getOnceMessage(tempListener) {
this._tempListener = tempListener;
}
off(key, fn) { //取消订阅
//delete只能删除对象自己的属性,不能删除其原型链上的属性,全局作用域中的函数不能被delete
if (this._listenerStore[key]) delete this._listenerStore[key];
}
async start() {//触发事件
// setTimeout(() => {
Object.values(this._listenerStore).forEach(arr =>
arr.forEach(_fun => _fun(Math.random() * 100 + '元'))
)
if (this._tempListener) {
this._tempListener('只订阅者一次性:' + Math.random() * 100 + '元');
delete this._tempListener;
}
// }, 5000);
}
}
let subscriber = new PubSubEvent();
subscriber.onMessage('张三', (price) => {
console.log('当前价格' + price);
})
let publisher = new PubSubEvent();
publisher.start();
迭代器模式3
思路:遍历对象,回调返回
- 内部迭代,如:重写forEach
var each = function(arr, callback) {
var result
for (var index = 0, len = arr.length; index < len; index++) {
result = callback.call(arr, index, arr[index])
//call(指定函数上下文对象thisobj,参数args)
if (result === false) {
break
}
}
}
each([1,2,3,3,4,5,6,7], function(index, item) {
if (item > 3) {
return false
}
})
- 外部迭代,如:Iterator,通过ltertor.next()迭代下一个元素,可以手工控制迭代过程或者顺序
var Iterator=function(obj){
var current=0;
var next=function(){
current +=1;
}
var isDone=function(){
return current >= obj.length
}
var getCurrent=function(){
return obj[current];
}
return {
next,
length:obj.length,
isDone,
getCurrent
}
}
var compare = function(iteraotr1, iteraotr2) {
if (iteraotr1.length !== iteraotr2.length) {
alert('不相等')
}
//外部设定条件来决定迭代器的进行
while (!iteraotr1.isDone() && !iteraotr2.isDone()) {
if (iteraotr1.getCurrent() !== iteraotr2.getCurrent()) {
alert('不相等')
}
iteraotr1.next()
iteraotr2.next()
}
alert('相等')
}
var iteraotr1 = Iterator([1,2,3])
var iteraotr2 = Iterator([1,2,3])
compare(iteraotr1,iteraotr2) // 相等