"好的"实现declare()函数的实践

"Good" practices to implement declare() function

本文关键字:quot 函数 declare 好的 实现      更新时间:2023-09-26

简介

目前,我对declare()函数的实现很好奇,这应该允许我使用原型继承来声明javascript类(或者类似的,因为javascript使用不同的对象模型,而不是经典的OOP(。到目前为止,我发现了一些问题,我想知道一些人的意见和澄清(如果可能的话(。

这里有一个"再现脚本">(简化以再现问题(,可以在控制台中执行:

function namespace(){
    if(arguments.length > 1){
        var m, map, result;
        for(m = 0; map = arguments[m], m < arguments.length; m++){
            result = namespace(map);
        }
        return result;
    }
    var scope = window,
        parts = arguments[0].split('.'),
        part, p;
    for(p = 0; part = parts[p], p < parts.length; p++){
        if(typeof scope[part] === 'undefined'){
            scope[part] = {};
        }
        scope = scope[part];
    }
    return scope;
}
function inherit(child, parent){
    child.prototype = Object.create(parent);
    child.prototype.constructor = child;
    child.prototype.$parent = parent.prototype;
}
function mixin(target, source){
    var value;
    target = target || {};
    if(typeof source == 'object'){
        for(var property in source){    
            target[property] = source[property];
        }
    }
    return target;
}
function extend(){
    var mixins = Array.prototype.slice.call(arguments, 0),
        object = mixins.shift() || {},
        length = mixins.length,
        m, mixin;
    for(m = 0; mixin = mixins[m], m < length; mixin(object, mixin), m++);
    return object;
}
function declare(config){
    var map  = config.object.split('.'),
        name = map.pop(),
        ns   = namespace(map.join('.'));
    ns[name] = function(){
        this.constructor.apply(this, arguments);
    };
    if(config.parent){
        if(typeof config.parent == 'string'){
            config.parent = namespace(config.parent);
        }
        inherit(ns[name], config.parent);
    }
    if(config.mixins){
        extend.apply(null, [ ns[name].prototype ].concat(config.mixins));
    }
    if(config.definition){
        mixin(ns[name].prototype, config.definition);
    }
}
declare({
    object: 'Test.A',
    definition: {
        constructor: function(){
            this.a = 1;
        },
        test: function(){
            return this.a;
        }
    }
});
declare({
    object: 'Test.B',
    parent: 'Test.A',
    definition: {
        constructor: function(){
            this.$parent.constructor.call(this);
            this.b = 1;
        },
        test: function(){
            return this.$parent.test.call(this) + this.b;
        }
    }
});
declare({
    object: 'Test.C',
    definition: {
        x: 1
    }
});
var a = new Test.A(),
    b = new Test.B();
console.log('a.test() = ' + a.test());
console.log('b.test() = ' + b.test());
// var c = new Test.C();

一个概念

declare()应当合并extend()inherit()mixin()功能的功能。作为一个自变量,它采用了一个config对象,该对象具有以下部分:

  1. object-对象类名(必需(
  2. parent-要继承的对象类名(不是必需的(
  3. mixins-objects/classes,哪些属性和方法需要包含在结果类/对象的原型中(不需要(
  4. definition-结果类原型属性和方法

问题


#1问题是关于构造函数的:如果config.definition没有constructor方法,那么我会得到RangeError: Maximum call stack size exceeded错误,这意味着我的"临时"构造函数

ns[name] = function(){
    this.constructor.apply(this, arguments);
};

开始在无尽的循环中呼唤自己。要复制,您可以取消对var c = new Test.C();行的注释。

问题:我是否应该在constructor方法存在的情况下测试config.definition,并注入一个空函数,因为没有指定constructor方法来避免这种情况?是否有其他可能的方法不会对性能产生重大影响


#2问题是关于调试的:当我试图记录ab变量时,我会在控制台中获得ns.(anonymous function){ ... },这意味着我在执行"动态声明"时丢失了名称空间和类/对象名称。

ns[name] = function(){ ... };

可能是匿名函数中没有名称的问题,所以浏览器试图保存发生赋值的最后一个符号。我希望有可能动态创建函数并为其定义名称,并发现了这个问题,建议使用eval();new Function(...)();

问题:是否有可能在没有任何evUl()魔力的情况下保存命名空间和类名

例如,以下是我想要的:

namespace('X.Y');
X.Y.Z = function(){ this.a = 1 };
var test = new X.Y.Z();
console.log(test);

显示:

X.Y.Z {a: 1}
^^^^^
Literaly, what I want to achieve.

我真的很感谢你的帮助。谢谢

我是否应该在构造函数方法存在的情况下测试config.definition,并注入一个空函数,因为没有指定构造函数方法来避免这种情况?是否有其他可能的方法不会对性能产生重大影响?

是的,注入一个空函数作为构造函数实际上会减少对性能的影响。

而不是function(){this.constructor.apply(this, arguments);}包装器,您应该只使用构造函数本身(除非您不确定它不会返回对象(:

ns[name] = config.definition && config.definition.constructor || function(){};

有没有可能在没有任何eval((魔术的情况下保存命名空间和类名?

没有。调试器/检查器在这里用于描述实例的是构造函数的.name。您不能通过使用命名函数来设置另一个,并且这些函数的名称中不能包含点。


#3问题是您的inherits函数。代替

child.prototype = Object.create(parent);

它需要是

child.prototype = Object.create(parent.prototype);