我是从Python和Smalltalk的背景来看Javascript,我很欣赏Self和Lisp在语言中的用法。使用ECMAScript5,我想在没有新操作符的情况下尝试使用原型OO。
约束:
以下是我在实施时尝试达到的标准:
function subclass(Class, Base) {
"use strict";
function create(self, args) {
if (!(self instanceof this))
self = Object.create(this.prototype);
var init = self.__init__;
return init ? init.apply(self, args) : self;
}
if (Base instanceof Function) Base = Base.prototype;
else if (Base===undefined) Base = Object.prototype;
Class.prototype = Object.create(Base);
Class.prototype.constructor = Class;
Class.create = create;
Class.define = function define(name, fn) { return Class.prototype[name] = fn; };
Class.define('__name__', Class.name);
return Class;
}
它似乎在一个简单的模型中工作:
function Family(){return Family.create(this, arguments)}
subclass(Family, Object);
Family.define('__init__', function __init__(n){this.name=n; return this;});
function Tribe(){return Tribe.create(this, arguments)}
subclass(Tribe, Family);
function Genus(){return Genus.create(this, arguments)}
subclass(Genus, Tribe);
function Species(){return Species.create(this, arguments)}
subclass(Species, Genus);
将该类用作工厂函数:
var dog = Species('dog');
console.assert(dog instanceof Object);
console.assert(dog instanceof Family);
console.assert(dog instanceof Tribe);
console.assert(dog instanceof Genus);
console.assert(dog instanceof Species);
或使用new运算符:
var cat = new Species('cat');
console.assert(cat instanceof Object);
console.assert(cat instanceof Family);
console.assert(cat instanceof Tribe);
console.assert(cat instanceof Genus);
console.assert(cat instanceof Species);
console.assert(Object.getPrototypeOf(dog) === Object.getPrototypeOf(cat))
我是否在实施过程中忽略了原型OO所需的功能?是否有我应该进行更改的Javascript约定或交互?总之,这里有什么“问题”,是否有明显的改进?
我想使用构造函数定义成为DRYer,但我发现函数的name属性不可写,这就是支持WebKit Inspector的对象名称。我能够构建一个eval来完成我想要的东西,但是......哎呀。
答案 0 :(得分:8)
编辑:哦,我现在看到了这个问题。答:不,你说的完全正确。设置函数名称的唯一方法是使用函数声明,这意味着在评估时。因此,您需要在源代码中使用它(eval是后门)。我先前回答了一个更简单的问题,但使用了相同的要点:Minor drawback with Crockford Prototypical Inheritance。关于此主题的另一个资源是http://kangax.github.com/nfe/
有使用displayName
属性的动作,以便将函数的不可更改名称与其调试器外观分开。这是在Firefox和其他一些东西中实现的,并且是一个包含在es6中的稻草人,但它还不是暂定规范的一部分:http://wiki.ecmascript.org/doku.php?id=strawman:name_property_of_functions
以下是一些来自Chrome工作人员的论文,内容涉及命名函数http://querypoint-debugging.googlecode.com/files/NamingJSFunctions.pdf
以下是铬问题,讨论为什么它尚未实施:http://code.google.com/p/chromium/issues/detail?id=17356
原来的回答:
你打算完成的任务,你做得很好。我做过类似类型的几个例子:
首先是一个简单的'遗传'功能,它允许你做以下的事情:
var MyObjCtor = heritable({
constructor: function MyObj(){ /* ctor stuff */},
super: SomeCtor,
prop1: val,
prop2: val,
/**etc..*/
});
function heritable(definition){
var ctor = definition.constructor;
Object.defineProperty(ctor, 'super', {
value: definition.super,
configurable: true,
writable: true
});
ctor.prototype = Object.create(ctor.super.prototype);
delete definition.super;
Object.keys(definition).forEach(function(prop){
var desc = Object.getOwnPropertyDescriptor(definition, prop);
desc.enumerable = false;
Object.defineProperty(ctor.prototype, prop, desc);
});
function construct(){
var obj = new (ctor.bind.apply(ctor, [].concat.apply([null], arguments)));
ctor.super.call(obj);
return obj;
}
construct.prototype = ctor.prototype;
return construct;
}
另一个是创建用于libffi(node-ffi)的结构。从本质上讲,你有构造函数继承和原型继承。您可以创建从构造函数继承的构造函数,这些构造函数创建结构的实例。 https://github.com/Benvie/node-ffi-tools/blob/master/lib/Struct.js
为了创建一个命名函数,需要使用eval,所以如果你需要一个命名的构造函数。我毫不犹豫地在需要的地方使用它。
答案 1 :(得分:4)
Shane我可以告诉你来自你的背景Javascript的行为方式与你尝试的方式不同。在你立即讨厌回答这个问题之前......
我喜欢OOP并且来自非常友好的背景。我花了一些时间来围绕Prototype对象(将原型方法看作静态函数......只能在初始化对象上工作)。在javascript中完成的许多事情都来自使用良好安全性和封装的语言非常“错误”。
使用“new”运算符的唯一真正原因是,如果要创建多个实例或尝试阻止某种类型的逻辑运行,直到触发事件。
我知道这不是答案......输入评论太过分了。要真正获得更好的视角,你应该创建对象和变量并查看你的DOM ......你会发现你可以从任何上下文中获得任何地方。我发现these articles非常有资源填写详细信息。
祝你好运。
<强>更新强>
因此,如果你确实需要一个工厂方法来创建同一个对象的不同实例,这里有一些我希望我听过的提示:
了解这两件事可以帮助您解决问题。为了保持DOM清洁并遵循良好的javascript开发实践,可以保持全局命名空间的清洁(DOM中的窗口引用)。这也会让你对所做的事情感觉“更好”,你觉得你所做的事情是“错误的”。并保持一致。
这些是我通过反复试验所学到的东西。如果你可以把它与经典的OO语言区别开来,Javascript是一种伟大而独特的语言。祝你好运!
答案 2 :(得分:3)
这可能无法解答您的问题,这可能并不是您想要的,但这是另一种关于OO JavaScript的方法。我喜欢它主要是因为语法;我更容易理解,但我没有你的背景。
// ------- Base Animal class -----------------------
var Animal = function(name) {
this.name = name;
};
Animal.prototype.GetName = function() {
return this.name;
};
// ------- End Base Animal class -----------------------
// ------- Inherited Dog class -----------------------
var Dog = function(name, breed) {
Animal.call(this, name);
this.breed = breed;
};
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
Dog.prototype.GetBreed = function() {
return this.breed;
};
Dog.prototype.Bark = function() {
alert(this.GetName() + ' the ' + this.GetBreed() + ' says "Arf!"');
};
// ------- End Inherited Dog class -----------------------
// Usage
var harper = new Dog('Harper', 'miniature pinscher');
harper.Bark();
答案 3 :(得分:1)
如果没有eval,则无法创建命名构造函数。所有你会发现的是一个构造函数,如果库实现名称空间,则带有一个带有名称的隐藏属性。
回答其余问题,有很多javascript类库。你可以自己编写,但如果你想以正确的方式去理解和实现正确的继承系统,那就太麻烦了。
我最近编写了自己的类库,试图改进所有库:Classful JS。我认为值得一看,因为它简单的设计,用法和其他一些改进,比如从子类中调用任何超级方法(我没有看到任何可以做到这一点的库,所有这些都只能调用正在进行的超级方法压倒一切)。