Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【JS设计模式系列】代理模式 #49

Open
laihuamin opened this issue Jul 23, 2019 · 0 comments
Open

【JS设计模式系列】代理模式 #49

laihuamin opened this issue Jul 23, 2019 · 0 comments

Comments

@laihuamin
Copy link
Owner

什么是代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。

代理模式的划分

按职责来划分的话,分为以下8种代理:

1、缓存代理

2、虚拟代理

3、写时复制Copy-on-Write 代理

4、保护(Protect or Access)代理

5、Cache代理

6、防火墙(Firewall)代理

7、同步化(Synchronization)代理

8、智能引用(Smart Reference)代理

在js中常用到的是缓存代理虚拟代理

虚拟代理

  • 不使用代理模式写图片预加载
// 不使用代理的预加载图片函数如下
var myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    var img = new Image();
    img.onload = function(){
        imgNode.src = this.src;
    };
    return {
        setSrc: function(src) {
            imgNode.src = "loading.gif";
            img.src = src;
        }
    }
})();
// 调用方式
myImage.setSrc("pic.png");
  • 使用代理模式写图片预加载
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("loading.gif");
            img.src = src;
        }
    }
})();
// 调用方式
ProxyImage.setSrc("pic.png");
  • 方案一步骤:

1、创建img标签

2、插入img标签

3、创建img对象

4、书写onloading方法

5、返回设置图片对象。

缺点

1、代码耦合度比较大,一个函数内负责做了几件事情。未满足面向对象设计原则中单一职责原则;

2、当某个时候不需要图片预加载的时候,需要从myImage 函数内把代码删掉,这样代码耦合性太高。

  • 方案二步骤:

1、myImage中创建img标签

2、myImage中插入img标签

3、myImage中返回设置imgNode的src方法

4、ProxyImage中创建img对象

5、ProxyImage中书写onload方法

6、ProxyImage中返回设置图片的方法。

优点

1、myImage 函数只负责做一件事。创建img元素加入到页面中,其中的加载loading图片交给代理函数ProxyImage 去做。

2、加载成功以后,代理函数ProxyImage 会通知及执行myImage 函数的方法。

3、当以后不需要代理对象的话,我们直接可以调用本体对象的方法即可

缓存代理

缓存代理,就是将前面使用的值缓存下来,后续还有使用的话,就直接拿出来用。

var add = function(){
    var sum = 0
    for(var i = 0, l = arguments.length; i < l; i++){
        sum += arguments[i]
    }
    return sum
}
var proxyAdd = (function(){
    var cache = {} //缓存运算结果的缓存对象
    return function(){
        var args = Array.prototype.join.call(arguments)
        if(cache.hasOwnProperty(args)){//等价 args in cache
            console.log('使用缓存结果')
            return cache[args]//直接使用缓存对象的“值”
        }
        console.log('计算结果')
        return cache[args] = add.apply(this,arguments)//使用本体函数计算结果并加入缓存
        console.log(cache);
    }
})()
console.log(proxyAdd(1,2,3,4,5))
console.log(proxyAdd(1,2,3,4,5))
console.log(proxyAdd(1,2,3,4,5))

// 输出结果
计算结果
15
使用缓存结果
15
使用缓存结果
15

两者的职责划分:add函数提供计算功能。proxyAdd提供访问add函数的功能和缓存功能。

es6中的proxy

proxy的用法

const target = {}, handler = {}
const proxy = new Proxy(target, handler)

target是目标对象,handler是处理函数。

handler的内建方法集

var handler = {
    get: function() {},
    set: function() {},
    apply: function() {},
    construct: function() {},
}

handler的get方法

参数:目标对象、属性名、proxy实例本身

例子

var person = {
  name: "张三"
};

var proxy = new Proxy(person, {
  get: function(target, property) {
    if (property in target) {
      return target[property];
    } else {
      throw new ReferenceError("Property \"" + property + "\" does not exist.");
    }
  }
});

proxy.name // "张三"
proxy.age // 抛出一个错误

handler的set方法

参数:目标对象、属性名、属性值、proxy实例本身

例子

let validator = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // 对于满足条件的 age 属性以及其他属性,直接保存
    obj[prop] = value;
  }
};

let a = {}

let person = new Proxy(a, validator);

person.age = 100;

a.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错

handler的apply方法

参数:目标对象,目标对象的上下文,参数组
例子

var target = function () { return 'I am the target'; };
var handler = {
  apply: function () {
    return 'I am the proxy';
  }
};

var p = new Proxy(target, handler);

p() // I am the proxy

当调用p函数时,就会被proxy拦截,返回I am the proxy

handler的construct方法

construct方法用于拦截new指令。

参数:目标对象、构造函数的参数对象
例子

var P = new Proxy(function () {}, {
  construct: function(target, args) {
    console.log('called: ' + args.join(', '));
    return { value: args[0] * 10 };
  }
});

const p = new P(1);
// "called: 1"
p.value
// 10

proxy重写图片预加载

let myImage = (function(){
    var imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    return {
        setSrc: function(src) {
            imgNode.src = src;
        }
    }
})();
let myImageProxy = new Proxy(myImage, {
    apply(target, ctx, arguments) {
        let img = new Image
        img.onload = function(){
            // 图片加载完成,正式加载图片
            target.call(ctx, ...arguments)
        }
        // 图片未被载入时,加载一张提示图片
        target.call(ctx, 'file://c:/loading.png')
        img.src = arguments[0]
    }
})

// 调用
let myImg = new myImageProxy(document)
myImg.setSrc('http://images/qq.jpg')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant