文章目录
原型模式
原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
因为JavaScript的特性, 这一点实现起来格外的简单
//父类
class Parent{
constructor(x){
this.x = x;
}
showX(){
alert( this.x );
}
}
//子类1继承
class ChildA extends Parent{
constructor(x,y){
super();
this.y = y;
}
showY(){
alert( this.y );
}
}
//子类2继承
class ChildB extends Parent{
constructor(x,z){
super();
this.z = z;
}
showZ(){
alert( this.z );
}
}
let obj = {
sayHello(){
alert( "Hello" );
}
};
let objA = Object.create(obj,{
name :{
writable:true,
configurable :true,
enumerable:true,
value : "AA"
}
});
let objB = Object.create(obj,{
name :{
writable:true,
configurable :true,
enumerable:true,
value : "BB"
}
});
objA.sayHello()
多个类使用到了相同的属性或方法,那我们就可以通过原型继承的方式来创造出类或者实例对象
策略模式
在现实中,很多时候有多种途径到达同一个目的地。
比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路。
在程序设计中,我们也常常遇到类似的情况,要实现某一个功能有多种方案可以选择
这些算法灵活多样,而且可以随意互相替换。这种解决方案就是所谓的策略模式.
策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
实例
针对不同绩效的员工发不同的工资
一个基于策略模式的程序至少由两部分组成。
- 第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。
- 第二个部分是环境类Context,Context 接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明Context 中要维持对某个策略对象的引用。
var strategies = {
//策略类
"S": function( salary ){
return salary * 4;
},
"A": function( salary ){
return salary * 3;
},
"B": function( salary ){
return salary * 2;}
};
//环境类Context
var calculateBonus = function( level, salary ){//接受客户的请求
return strategies[ level ]( salary );//委托类
};
console.log( calculateBonus( 'S', 20000 ) ); // 输出:80000
console.log( calculateBonus( 'A', 10000 ) ); // 输出:30000
mtween缓动动画
现在来分析实现这个程序的思路。在运动开始之前,需要提前记录一些有用的信息,至少包括以下信息:
- 动画开始时,小球所在的原始位置;
- 小球移动的目标位置;
- 动画开始时的准确时间点;
- 小球运动持续的时间。
运动曲线算法
- c , b , d为常数, t为变量
我们假设c为2, b为3,t的取值范围为[0, d]
/*
* 这是一些运动曲线, 模拟出近似的贝塞尔效果
* @t: 动画已消耗的时间
* @b: 初始位置
* @c: 结束位置
* @d: 动画持续的总时间
* */
var tween = {
linear: function (t, b, c, d) {
return c * t / d + b;
},
easeIn: function (t, b, c, d) {
return c * (t /= d) * t + b;
},
strongEaseIn: function (t, b, c, d) {
return c * (t /= d) * t * t * t * t + b;
},
strongEaseOut: function (t, b, c, d) {
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
},
sineaseIn: function (t, b, c, d) {
return c * (t /= d) * t * t + b;
},
sineaseOut: function (t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
}
};
代理模式
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问
代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象
保护代理和虚拟代理
- 当客户发送至代理的一个请求在经过分析后觉得不符合条件, 结果被拒绝了, 这个就是保护代理
- 当客户发送至代理的一个请求, 该请求需要代理判断某些条件满足后才会向目标发送最终的指令, 那么这个就是虚拟代理
实例:虚拟代理实现图片懒加载
<script>
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(this.src);
};
return {
setSrc: function (src) {
myImage.setSrc('./1.png');
img.src = src;
}
}
})();
proxyImage.setSrc('./2.png');
</script>
在Web 开发中,图片预加载是一种常用的技术
如果直接给某个img 标签节点设置src 属性,由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。
常见的做法是先用一张loading 图片占位,然后用异步的方式加载图片
等图片加载好了再把它填充到img 节点里,这种场景就很适合使用虚拟代理
代理的意义:单一职责原则
就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变化的原因。
如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能会有多个。
面向对象设计鼓励将行为分布到细粒度的对象之中,如果一个对象承担的职责过多,等于把这些职责耦合到了一起,这种耦合会导致脆弱和低内聚的设计。
当变化发生时,设计可能会遭到意外的破坏。
缓存代理
缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果。
var multi = function () {
console.log('开始计算乘积');
var a = 1;
for (var i = 0, l = arguments.length; i < l; i++) {
a = a * arguments[i];
}
return a;
};
var proxyMulti = (function () {
var cache = {};
return function () {
var args = Array.prototype.join.call(arguments, ',');
if (args in cache) {
return cache[args];
}
return cache[args] = multi.apply(this, arguments);
};
})();
console.log(proxyMulti(1, 2, 3, 4));
console.log(proxyMulti(1, 2, 3, 4));
开始计算乘积
24
24
Proxy es6内置的代理模式
Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作。 Vue3.0 中将会通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。
let p = new Proxy(target, handler)
target
代表需要添加代理的对象,handler
用来自定义对象中的操作,比如可以用来自定义 set 或者 get 函数。
let onWatch = (obj, setBind, getLogger) => {
let handler = {
set(target, property, value, receiver) {
setBind(value, property)
return Reflect.set(target, property, value)
},
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver)
}
}
return new Proxy(obj, handler)
}
let obj = { a: 1 }
let p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`)
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`)
}
)
p.a = 2 // 控制台输出:监听到属性a改变
p.a // 'a' = 2
自定义 set 和 get 函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。
当然这是简单版的响应式实现,如果需要实现一个 Vue 中的响应式,需要我们在 get 中收集依赖,在 set 派发更新,之所以 Vue3.0 要使用 Proxy 替换原本的 API 原因在于 Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy 可以完美监听到任何方式的数据改变,唯一缺陷可能就是浏览器的兼容性不好了。