【发布时间】:2014-04-30 14:30:16
【问题描述】:
简介
目前我对 declare() 函数的实现感到好奇,这应该允许我使用原型继承(或某种继承,因为 javascript 使用不同的对象模型,而不是经典的 OOP)来声明 javascript 类。到目前为止,我发现了一些问题,我想知道某人的意见和澄清(如果可能的话)。
这是一个“重现脚本”(简化以重现问题),可以在控制台中执行:
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 对象:
- object - 对象类名(必填);
- parent - 要继承的对象类名(非必需);
- mixins - 对象/类,结果类/对象的原型中需要包含哪些属性和方法(非必需);
- 定义 - 结果类原型属性和方法。
问题
#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 问题 与调试有关:当我尝试记录 a 或 b 变量时,我在控制台中收到 ns.(anonymous function){ ... },这意味着,我已经在执行“动态声明”时丢失了命名空间和类/对象名称。
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.
我非常感谢您的帮助。谢谢。
【问题讨论】:
标签: javascript object definitions