【问题标题】:JavaScript: Adding a Namespace to Prototype MethodsJavaScript:向原型方法添加命名空间
【发布时间】:2014-07-20 16:11:13
【问题描述】:

我有一个商业应用程序,它具有使用原型链的现有 JavaScript 对象结构。通过向对象原型添加更多方法,我已经成功地扩展了这个 API。但是,我意识到最好在我的方法前面添加一个命名空间,以防应用程序供应商决定在未来版本中将新方法命名为与我的方法之一相同。

如果我有一个名为 State 的现有对象,我将添加一个名为 getPop 的方法,如下所示:

State.prototype.getPop = function(){return this.pop;};

var Washington = new State('Washington',7000000);

Washington.getPop(); //returns 7000000

我想要做的是在我的自定义方法之前添加一个名为“cjl”的命名空间以避免名称冲突,以便我可以这样调用它:

Washington.cjl.getPop();

我试过了:

State.prototype.cjl = {};
State.prototype.cjl.getPop = function(){return this.pop;};

问题是这个。它不指向实例,而是指向“cjl”对象。

我尝试了各种方法,包括使用 .bind(),但它们似乎都不起作用。我终于在这里找到了答案:Is it possible to organise methods on an object's prototype into namespaces? 这使用 Object.defineProperty() 方法工作。问题是商业应用程序只能在 IE 的兼容模式下工作,不支持非 DOM 元素的 Object.defineProperty() 方法。

还有其他方法可以做到这一点吗?我不想调用多个函数,这是一些技术的结果,例如:

Washington.cjl().getPop();

【问题讨论】:

  • 最好在方法名称前加上前缀。没有真正干净的解决方案可以做你想做的事。
  • 只做Washington.cjl_getPop(); 要好得多,因为它更容易实现且执行速度更快,并且确实没有比您提议的名称冲突的可能性更大(实际上,它可能甚至更少容易发生碰撞)。在原型上使用子对象并不简单。
  • 嗯,我有点庆幸这并不简单,因为试图让它发挥作用让我质疑我的中级 JavaScript 技能的合法性。我想让使用它的人尽可能轻松和干净地使用我的扩展程序,这就是为什么我想尽可能避免使用前缀方法,但我可能只是依靠它。

标签: javascript namespaces prototype


【解决方案1】:

您可以通过以下方式命名空间,读取您的 cmets 我发现您无法更改原始构造函数,因此您必须用自己的构造函数替换原始构造函数并将原始构造函数保存在闭包中。

每个状态实例都有自己的 cjl 实例,但它只引用当前状态实例,所有 cjl 函数都是共享的,因为它们只存在一次:

[更新] 忘记在 myState 的原型链中获取 State.prototype。

//the original constructor
function State(name, pop){
  this.name=name;this.pop=pop;
}
State.org="original constructor";

//original constructor is available through
//  closure and window.State is replaced with
//  your constructor having the cjl namespace
(function(State){
  //cjl namespace
  function cjl(stateInstance){
    this.stateInstance=stateInstance;
  };
  //cjl functions
  cjl.prototype.getPopInThousands=function(){
    //do not use this, instead use this.stateInstance
    return this.stateInstance.pop/1000;
  }
  function myState(){
    //apply State constructor
    State.apply(this,arguments);
    //create a clj instance, all methods
    //  are on cjl.prototype so they're shared
    this.cjl = new cjl(this);
  }
  //inherit from State (use polyfil for older browsers)
  myState.prototype = Object.create(State.prototype);
  //replace window.State with your constructor
  window.State=myState;
}(State))

var s = new State("Wasington", 7000000);
console.log(s.cjl.getPopInThousands());
//non standard, name
console.log("constructor name",s.constructor.name);
console.log("constructor tostring",s.constructor.toString());

更多关于构造函数和原型可以在这里找到:https://stackoverflow.com/a/16063711/1641941

我必须同意朋友和 cookie 的观点,即预先固定函数名称可能是更好的解决方案,但是如果您想对名为 Country 的对象使用相同的方法,那么您可能会考虑使用以前的代码,因为您可以重复使用cjl 对象。

【讨论】:

  • 哇。感谢那。我不确定我是否会做到这一点,但我将对其进行全面分析以提高我的理解。我有一个问题。如果在我的代码触发时对象已经实例化,这会起作用吗?这就是我必须处理的情况。
  • @Johnsonium 如果您希望已经存在的实例具有这些功能,则不能使用上面的代码。该代码用其他东西替换了构造函数,并且已经创建的实例是用旧的构造函数创建的,所以不会有你添加的函数。在您的情况下,您必须改变 State.prototype 并使用已经建议的名称前缀:State.prototype.cji_getPop=function(){...
【解决方案2】:

不要在函数外部定义State.prototype.cjl,而是尝试在构造函数内部设置cjl“命名空间”。

function State(){
    var thisObject = this;

    this.cjl = {
        getPop: function(){
            return thisObject.pop;
        }
    };
}

那你就可以Washington.cjl.getPop();了。

【讨论】:

  • 您正在失去原型继承的所有好处。还不如完全跳过它并创建一个对象工厂。
  • 不幸的是,我没有能力修改构造函数,因为它是精心设计和混淆的。即使可以,我也不想更改它,因为我的意图是扩展提供的功能,而不是修改它。
【解决方案3】:

试试:

var State = function(name, pop) {
  this.name = name;
  this.pop = pop;
};

State.prototype.cjl = function(method) {
  return this.cjlDefs[method].apply(this, Array.prototype.slice.call(arguments, 1) );
};

State.prototype.cjlDefs = {
  getPop: function() {return this.pop;}
};


var Washington = new State('Washington', 80000);
console.log( Washington.cjl('getPop') );

https://jsfiddle.net/ghbjhxyh/

如果您愿意,也可以选择其他形状:

var State = function(name, pop) {
  this.name = name;
  this.pop = pop;
};

State.prototype.cjl = function(method) {
  this.cjlDefs.obj = this;
  return this.cjlDefs;
};

State.prototype.cjlDefs = {
  assertObj: function() { /* Make sensible assertion */ },
  getPop: function() { this.assertObj(); return this.obj.pop; }
};

var Washington = new State('Washington', 75000);
console.log( Washington.cjl().getPop() ); // 75000

https://jsfiddle.net/7vjrz2mn/

【讨论】:

    猜你喜欢
    • 2010-09-24
    • 2011-06-03
    • 2014-05-26
    • 1970-01-01
    • 2021-05-30
    • 2014-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多