由于一个对象不能直接引用另外一个对象,所以需要通过代理对象在这两个对象之间起到中介作用
适用 场景:
- 保护目标对象:客户端 只与 代理类 进行交互,不清楚 目标对象 的具体细节; 相当于 租客 只与 中介 进行交互,不知道房东的信息;
- 增强目标对象:代理类 在 目标对象的基础上,对 目标对象的功能 进行增强;
优点:
- 分离目标对象:代理模式 能将 代理对象 与 真实被调用的 目标对象 分离;
- 降低耦合:在一定程度上,降低了系统耦合性,扩展性好;
- 保护目标对象:代理类 代理目标对象的业务逻辑,客户端 直接与 代理类 进行交互,客户端 与 实际的目标对象之间没有关联;
- 增强目标对象:代理类 可以在 目标对象基础上,添加新的功能;
缺点:
- 类个数增加:代理模式 会造成系统中 类的个数 增加,比不使用代理模式增加了代理类,系统的复杂度增加;(所有的设计模式都有这个缺点)
- 性能降低:在 客户端 和 目标对象 之间,增加了一个代理对象,造成 请求处理速度变慢;
类图
实现代码
class Goole {
constructor() { }
get() {
return 'google';
}
}
class Proxy {
constructor() {
this.google = new Goole();
}
get() {
return this.google.get();
}
}
let proxy = new Proxy();
let ret = proxy.get();
console.log(ret);
经典场景
事件委托
事件捕获指的是从 document 到触发事件的那个节点,即自上而下的去触发事件;
事件冒泡是自下而上的去触发事件
绑定事件方法的第三个参数,就是控制事件触发顺序是否为事件捕获,true 为事件捕获;false 为事件冒泡,默认 false;
<body>
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
let list = document.querySelector('#list');
list.addEventListener('click', event => {
alert(event.target.innerHTML);
});
</script>
</body>
图片懒加载
-
服务端
let express = require('express'); let path = require('path') let app = express(); app.get('/images/loading.gif', function (req, res) { res.sendFile(path.join(__dirname, req.path)); }); app.get('/images/:name', function (req, res) { setTimeout(() => { res.sendFile(path.join(__dirname, req.path)); }, 2000); }); app.get('/', function (req, res) { res.sendFile(path.resolve('index.html')); }); app.listen(8080);
-
客户端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> <style> * { padding: 0; margin: 0; } ul, li { list-style: none; } #background { position: absolute; border: 1px solid green; right: 10px; top: 10px; } #background li { float: left; margin-left: 10px; text-align: center; border: 1px solid red; border-radius: 5px; } #myImage { width: 600px; height: 400px; margin: 100px auto; } #myImage img { width: 100%; height: 100%; } </style> </head> <body> <ul id="background"> <li data-src="/images/bg1.jpg">图片1</li> <li data-src="/images/bg2.jpg">图片2</li> </ul> <div id="myImage"></div> </body> <script> let container = document.querySelector("#background"); let myImage = document.querySelector("#myImage"); let Background = (function () { let img = new Image(); myImage.appendChild(img); return { set(src) { img.src = src; }, }; })(); // 代理 let LoadingBackground = (function () { let img = new Image(); img.onload = function () { Background.set(this.src); }; return { set(src) { Background.set(`/images/loading.gif`); img.src = src; }, }; })(); container.addEventListener("click", function (event) { let src = event.target.dataset.src; LoadingBackground.set(src + "?ts=" + Date.now()); }); </script> </html>
防抖代理
跨域代理
$.proxy
接受一个函数,然后返回一个新函数,并且这个新函数始终保持了特定的上下文语境(类似 bind);
jQuery.proxy(function, context) function 为执行的函数,content 为函数的上下文,this 值会被设置成这个 object 对象;
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
let btn = document.getElementById('btn');
btn.addEventListener('click', function () {
setTimeout($.proxy((function () {
$(this).css('color', 'red');
}), this), 1000);
// 等价
// function setColor() {
// $(this).css('color', 'red');
// }
// setColor = setColor.bind(this);
// setTimeout(setColor, 1000);
});
</script>
Proxy
Proxy 用于修改某些操作的默认行为;
Proxy 可以理解成,在目标对象之前架设一层 “拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写;
let wang = {
name: 'wangy',
age: 29,
height: 165
}
let wangMama = new Proxy(wang, {
get(target, key) {
if (key == 'age') {
return wang.age - 1;
} else if (key == 'height') {
return wang.height + 5;
}
return target[key];
},
set(target, key, val) {
if (key == 'boyfriend') {
let boyfriend = val;
if (boyfriend.age > 40) {
throw new Error('太老');
} else if (boyfriend.salary < 20000) {
throw new Error('太穷');
} else {
target[key] = val;
return true;
}
}
}
});
console.log(wangMama.age);
console.log(wangMama.height);
wangMama.boyfriend = {
age: 41,
salary: 3000
}
装饰器模式
上一篇