用于构造代理对象的JavaScript模式,使用经典继承

JavaScript pattern for constructing proxy objects, using classical inheritance

本文关键字:经典 继承 模式 JavaScript 代理 对象 用于      更新时间:2023-09-26

虽然没有最终确定,但我正在试验ES6代理。我的目标是有一个构造函数(像下面所示的那样利用经典继承的构造函数)来创建具有继承链的代理对象。

function inherit(child, parent){ //a classical inheritance pattern
  var F = function(){};
  F.prototype = parent.prototype; 
  child.prototype = new F(); 
  child.parent = parent.prototype; 
  child.prototype.constructor = child;
  return child;
}

下面的内容看起来合理吗?我创建了一个项目,并从构造函数返回代理。任何将返回对实例的引用(用于链接目的)的方法都必须返回对代理的引用,否则在链接时将丢失代理。

function Item(attrs){
  this.attrs = attrs;
  var proto = this.constructor.prototype;
  return this.proxy = Proxy.create(new MyHandler(this), proto);
}
Item.prototype.setStatus = function(status){
  //do work
  return this.proxy; //we do this everywhere instead of a simple 'this'?
}
function DVD(attrs){
   attrs.type = 'DVD';
   return Item.call(this, attrs);
}
inherit(DVD, Item);
var negotiator = new DVD({title: 'The Negotiator'}); //returns proxy

目标是构造代理对象(通过'new'关键字),这些对象可能是经典继承链的产物。

下一个问题。考虑一下如果我用Backbone.Events扩展Item原型会发生什么。那些导入的方法将返回this而不是this.proxy。为了解决这个问题,我必须包装返回this的所有导入方法,以便返回proxy。这似乎很痛苦,而且容易出错。使用代理抽象涉及到大量的连接,这对我来说似乎不太正确。

虽然我喜欢代理概念,但我关心的是它的实用性。也许我错过了一些实现目标的更好的练习?

编辑:

这个例子过于简化了,因此无法描述我为什么需要它。

最终,我使用代理的目标是允许多重继承。我希望能够混合多个行为(模块)到一个给定的代理对象。我也希望它能很简单地分解行为的心血来潮。

通过使用代理,我可以让代理对象管理对方法的访问,并根据需要将信息传递给mixins。基本上,这允许改进面向方面的编程。对于方面,我通常会包装和替换函数。有时,一个给定的方法被几个方面包装和重新包装。AOP的问题在于,一旦以这种方式修改了对象,就不容易删除单个方面,因为该方面可能已经被重新包装所掩盖。

代理没有这个问题。你的mixins可以放在一个数组中。代理处理对这些mixins中找到的方法的调度(甚至是多个方法,因此是多重继承)。那么反混合行为就像从数组中删除mixin一样简单。

代理的问题在于,虽然方法返回对对象本身的引用(用于链接目的)是一种常见的做法,但您实际上不希望任何原始方法返回对该对象的引用(从而绕过代理),而是希望返回代理。

您使用代理创建mixins的目标不是一个坏主意(请参阅这篇博客文章)。然而,你指出的问题,我认为这是你的主要问题,实际上根本不是问题。下面的代码例如:

var o, p;
function Obj() {  }
Obj.prototype.whoAmI = function() {
    console.log("I am the "+ (this == o ? "object" : "proxy"));    
    return this;
};
o = new Obj(); 
var p = new Proxy(o, {});
o.whoAmI().whoAmI();
p.whoAmI().whoAmI();

将输出:

I am the object
I am the object
I am the proxy
I am the proxy

可以看到,在目标对象中使用的this关键字实际上是代理。因此,如果目标对象返回this,它实际上返回代理。正是你想要的。

如果你想绝对确定代理对象被返回,你可以像这样包装你的代理方法:

var o, p;
function Obj() {  }
Obj.prototype.whoAmI = function() {
    console.log("I am the "+ (this == o ? "object" : "proxy"));    
    return o; // bit of a code smell here
};
o = new Obj(); 
var p = new Proxy(o, {
    get: function(target, name) {
        return function() {
            var rtnVal = target[name].call(this);
            return rtnVal == target ? this : rtnVal;
        }
    }
});
p.whoAmI().whoAmI(); 

将输出:

I am the proxy
I am the proxy

这确保返回的对象永远不是未包装的对象。

所有这些当然不是非常有利于方法调用的性能。相反,我建议使用更传统的javascript mixins方法。据我所知,在飞行中打开和关闭mixins的真正好的用例并不多。

但是如果您只想创建像$。代理可以做到,但不想使用jQuery,你可以这样做:

var context = this;
something.addSomeListener( function(){ context.myListener.apply( context, arguments ); } );