javascript设计模式-代理模式

代理的结构

代理模式的最基本形式就是对访问进行控制。代理对象和另一个对象实现的是同样的接口。代理对象只是节制对本体的访问。
代理对象不会在另一个对象上添加或修改方法,也不会简化对象的接口,所有对方法的调用都会被传递给本体。

虚拟代理

用于控制对那种创建开销很大的本体的访问。他会把本体的实例化推迟到有方法被调用的时候。
代理对象具有与本体类实例对象的相同的方法,想操作本体类实例对象必须先通过代理对象,从而实现对本体类实例对象的控制。

创建虚拟代理的通用模式

  1. 先创建一个动态代理类的壳体及initialize和checkInitialization两个方法
  2. 创建派生子类
  3. 设置子类的class属性为本体类
  4. 设置触发实例化条件
// 抽象类
function Dynamic() {
	this.args = arguments;
	this.initialized = false;
	if(typeof this.class !== 'function '){
		throw new Error('err');
	}
	let that = this;
	for(let key in this.class.prototype) {
		if(typeof this.class.prototype[key] !== 'function'){
			continue;
		}
		(function (methodName){
			that[methodName] = function () {
				if(!that.initialized){
					return;
				}
				return that.subject[methodName].apply(that.subject, arguments);
			}
		})(key);
	}
}
Dynamic.prototype = {
	_initialize(){
		this.subject = {};
		this.class.call(this.subject, this.args);
		this.subject.__proto__ = this.class.prototype;
		this.interval = setInterval(()=>{
			this._checkInitialized();
		},100);
	},
	_checkInitialized(){
		if(this._isInitialized()){
			clearInterval(this.interval);
			this.isInitialized = true;
		}
	},
	_isInitialized(){
		throw new Error('error');
	}
}

// 本体类
function TestClass() {}
TestClass.prototype.add = function (){
	return 1 + 1;
}
TestClass.prototype.sub = fuunction () {
	return 2 - 1;
}

// 对本体类的代理
function TestProxy() {
	this.class = TestClass;
	// 触发初始化的条件
	document.getElementById('test-link').addEventListener('click', ()=>{
		this._initialize();
	});
	TestProxy.superclass.constructor.apply(this, arguments);
}

TestProxy.prototype._isInitialized = function (){
	if(this.subject){
		return true;
	}
}

// 继承函数
function extend(subClass, supClass){
	function F(){}
	F.prototype = supClass.prototype;
	subClass.prototype = new F();
	subClass.prototype.constructor = subClass;
	subClass.superclass = supClass.prototype;
}
extend(TestProxy, DynamicProxy);

示例:图片预加载

let myImage = (function (src) {
  let node = document.createElement('img');
  document.body.appendChild(node);
  return {
    setSrc(src){
      node.src = src;
    }
  }
})();

let proxyImg = (function (){
  let img = new Image();
  img.onload = function (){
    myImage.setSrc(this.src);
  }
  return {
    setSrc(src){
      myImage.setSrc('');
      img.src=src;
    }
  }
})();

示例:合并http请求

当有多个多选框,用户点击多选框进行上传文件时,当用户点击的过快,会导致服务器压力过大,此时需要通过虚拟代理,减少在某一时刻发送的请求数量。

let syncrequest = function (ids) {
  console.log('开始同步文件', ids);
}

let proxSync = (function () {
  let cache = [],
    timer;
  return function (id) {
    cache.push(id);
    if (timer) {
      return;
    }
    timer = setTimeout(() => {
      syncrequest(cache.join(','));
      clearTimeout(timer);
      timer = null;
    }, 2000);
  }
})();

let checkbox = document.getElementsByTagName('input');
for(let i = 0, c; c = checkbox[i++];){
  c.onclick = function(){
    if(this.checked === true){
      proxSync(this.id);
    }
  }
}

远程代理

远程代理用于访问另一个环境中的对象。
这种方式很难照搬到JavaScript中:

  1. 通常JavaScript运行环境不能长时间存在
  2. JavaScript中无法建立到另一个环境的套接字连接以访问其变量空间

控制对其他语言中的本体的访问。这种本体可能是Web服务资源,也可能是PHP对象,很难说你所使用的是什么模式。

安全代理

安全代理:用来控制真实对象的访问权限,即满足条件的可以访问,不满足条件的不能访问


class Order {
    constructor(productName, productCount, orderUserName) {
      this.productName = productName;
      this.productCount = productCount;
      this.orderUser = orderUserName;
    }

    // 获取订单中产品的名称
    getProductName() {
      return this.productName;
    }

    // 设置订单中产品的名称
    setProductName(productName) {
      this.productName = productName;
    }

    // 获取订单的用户
    getOrderUserName() {
      return this.orderUserName;
    }


    // 设置订单的用户
    setOrderUserName(orderUserName) {
      this.orderUserName = orderUserName;
    }

    // 获取产品数量
    getProductCount() {
      return this.oproductCount;
    }

    // 设置产品数量
    setProductCount(productCount) {
      this.productCount = productCount;
    }
  }

  class ProxyObject {
    constructor(realObject) {
      this.realObject = realObject;
    }

    // 获取订单中产品的名称
    getProductName() {
      if (userName === this.realObject.orderUserName) {
        return this.realObject.productName;
      }
    }

    // 设置订单中产品的名称
    setProductName(productName, userName) {
      if (userName === this.realObject.orderUserName) {
        this.realObject.productName = productName;
      }
    }

    // 获取订单的用户
    getOrderUserName() {
      if (userName === this.realObject.orderUserName) {
        return this.realObject.orderUserName;
      }
    }


    // 设置订单的用户
    setOrderUserName(orderUserName, userName) {
      if (userName === this.realObject.orderUserName) {
        this.realObject.orderUserName = orderUserName;
      }
    }

    // 获取产品数量
    getProductCount() {
      return this.realObject.productCount;
    }

    // 设置产品数量
    setProductCount(productCount, userName) {
      if (userName === this.realObject.orderUserName) {
        this.realObject.productCount = productCount;
      }
    }
  }
  let order = new Order('西瓜', 1, 'wang');

  let subject = new ProxyObject(order);
  let productCount = subject.getProductCount(order.orderUserName);
  console.log(productCount);

缓存代理

缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果

以计算乘积的程序为例,mult()方法用于计算乘积并返回计算后的结果,proxyMult用于把mult()计算后的结果缓存,当有相同的参数时,把参数对应的结果返回,如果没有相同的参数,把参数的乘积计算后缓存然后返回结果。

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 () { 
    var args = Array.prototype.join.call(arguments, ', '); 
    if (args in cache) { 
      return cache[args]; 
    } 
    return cache[args] = mult.apply(this, arguments); 
  } 
})(); 

proxyMult(1, 2, 3, 4);    
// 输出:24      
   
proxyMult( 1, 2, 3, 4 );    
// 输出:24

代理模式的适用场合

虚拟代理是一种优化模式。如果有些类或对象需要使用大量内存保存其数据,而你并不需要在实例化后立即访问这些数据, 或者, 构造函数需要进行大量计算,此时需要使用虚拟代理模式。

优缺点

优点

控制开销较大的对象的创建时机。

缺点

代理可以隐藏大量复杂行为。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端御书房

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值