javascript - "Good"实现 declare() 函数的实践

标签 javascript object definitions

简介

目前我对 declare() 的实现感到好奇函数,它应该允许我使用原型(prototype)继承声明 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 - 对象/类,哪些属性和方法需要包含到结果类/对象的原型(prototype)中(不是必需的);
  4. 定义 - 结果类原型(prototype)属性和方法。

问题


#1 问题 是关于构造函数的:if config.definition没有 constructor方法,然后我得到 RangeError: Maximum call stack size exceeded错误,这意味着我的“临时”构造函数

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

开始无限循环地调用自己。要重现,您可以取消注释 var c = new Test.C();行。

问题: 我应该测试 config.definitionconstructor方法存在并注入(inject)一个空函数,其中没有 constructor指定的方法来避免这种情况?是否有任何其他可能的方法而不会显着影响性能?


#2 问题 是关于调试的:当我尝试记录 a 时或 b变量然后我得到 ns.(anonymous function){ ... }在控制台中,这意味着我在执行“动态声明”时丢失了 namespace 和类/对象名称。

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

可能是没有名字的匿名函数的问题,所以浏览器会尝试保存最后的符号,这是赋值发生的地方。我希望有可能动态创建函数并为其定义名称,并找到了 this question , 建议使用 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.

非常感谢您的帮助。谢谢。

最佳答案

Should I test config.definition on constructor method existence and inject an empty function, where there is no constructor method specified to avoid this? Is there any other possible approaches without significant performance impact?

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

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

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

Is there any possibility to save namespace and classname without any eval() magic?

没有。您的调试器/检查器在这里用于描述实例的是 .name of the constructor function .您不能使用命名函数设置另一个,并且它们的名称中不能包含点。


#3 问题 是您的继承 功能。而不是

child.prototype = Object.create(parent);

应该是

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

关于javascript - "Good"实现 declare() 函数的实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22588982/

相关文章:

javascript - 使用 const 来要求模块

javascript - 从 zip 文件在 AWS 中创建 lambda 函数

java - 如何将新对象添加到 ArrayList?

java - 使用不同名称声明多个对象

function - 有没有办法在 Lua 中将本地函数按任意顺序排列?

javascript - 具有不同模式单位的 svg

javascript - Grafana 5插件开发,如何保存用户数据/面板状态?

delphi - 通过扩展类来设计组件样式

javascript - sendgrid 的 typescript 定义

c++ - 如何为 Product* getProductFromID(std::string) 编写方法定义;