Bounty编辑:
我正在寻找用纯原型OO范例编写的代码(想想Self)。不是原型OO和经典OO的混合物。我不想看到通用的OO包装器,而只是使用原型OO技术和仅原型OO技术。
参考相关问题:
在上面的问题中我主要关注
可以像这样编写原型OO吗?
我们是否需要构造函数和初始化逻辑,有哪些替代方案?
新问题:
在大型开源项目中,基本上有没有 javascript原型 OO的好例子?
澄清:
我必须澄清我对原型OO的意思:
进一步澄清原型OO:
JavaScript中的原型OO与经典OO仿真之间的区别是非常灰色的区域。这不是我值避免使用经典的OO。我想以学术的方式学习原型OO本身,而不学习经典OO仿真和原型OO的(可能更优化)组合。
这就是为什么我“禁止”课程,只是为了让我能够以纯粹的方式看到这些技术并扩展我自己的OO工具包。
示例:
像jQuery这样的热门示例无法满足第二个标准。 jQuery
对象是一个大类仿真。它侧重于从类创建新对象,而不是克隆现有对象。
如果我真的知道使用“pure”原型OO的任何例子,我会告诉你。我相信99%的JavaScript OO受到经典仿真的影响太大。
加分
如果
我还将接受有关如何编写超出琐碎的hello world应用程序的原型OO代码的文章/教程和示例。
答案 0 :(得分:9)
你找不到它。
我前段时间寻找这种事情,这就是我发现的:Self Paper Organizing Programs Without Classes(请看Citeseer for a PDF version。)本文讨论 Self <的最佳做法/ em>,原始的原型语言,最佳实践是使用“traits object idiom”,这是让你的对象继承自只包含方法的“traits对象”,而不是对象特定的数据。换句话说,一个可疑的类似于对象。
即使是原始的原型语言也会模仿类。
答案 1 :(得分:4)
你看过OMeta / JS吗? OMeta是一种基于Smalltalk和Self的实验研究模式匹配语言。 OMeta / JS是使用原型OO的javascript实现。
很好的评论和记录很多例子。它也在Github上。
http://tinlizzie.org/ometa-js/
https://github.com/alexwarth/ometa-js
编辑:OMeta是Alexander Warth博士学位的结果。
答案 2 :(得分:1)
我不确定你在寻找什么,但我目前的框架允许你像OO一样编程:
Cin.define({
name: 'MyApp.Logger',
extends: 'Cin.Component',
implements: ['MyApp.ILogger'],
mixes: {
SomeMixin: 'MyApp.SomeMixin'
},
init: function() {
},
method: function() {
},
statics: {
staticMethod: function() {}
}
});
然后您可以编写如下代码:
var instance = new MyApp.Logger();
instance.method();
MyApp.Logger.staticMethod();
我不是想在这里模仿经典OO。我试图创建一个方便有用的方法来声明继承,mixins,接口和一般OO概念,以便开发人员可以轻松编写这样的OO代码。这也让我有机会完成我的自动加载组件,这样你就不再需要处理依赖关系,而且你可以自定义构建并享受更快的开发,这要归功于每页加载不需要加载100个脚本。
如果你想学习原型OO概念,我认为你应该编写某种继承系统。请查看Dojo Toolkit或ExtJS。值得记住的是,基于Prototype的系统扭曲和破坏,它们比基于类的OO语言更强大。在我看来,没有一种正确的方法来编写原型代码。
我担心大多数(如果不是全部)继承系统可能看起来像是模仿经典的OO。在我看来,我的框架没有,但它甚至没有完成。
答案 3 :(得分:1)
可能JSLint(Crockford是原型继承的支持者,但我没有梳理过它的每一寸)。它看起来比面向对象更具功能性,但我希望通常情况下代码真正包含原型继承。
答案 4 :(得分:1)
在我的框架中,一切都是对象或“界面”。
接口定义了对象可能具有的公共函数(methods / property_gets / property_sets)。
您可以创建如下界面:var some_interface = GetInterface(constructor, interface_setup, parent_interface..)
您可以指定任意数量的parent_interfaces。因此,如果interface_A
继承interface_B
和interface_C
,您可以创建interface_A:GetInterface(constructor, interface_setup, interface_B, interface_C);
如果interface_A
继承interface_X
而interface_X
继承interface_Y
,那么interface_A
将拥有interface_X
和{{1}的所有功能有}。
不需要构造函数的接口会将构造函数参数保留为null。 interface_setup是一个如下所示的函数:
interface_Y
proto 参数指向的对象有4种方法:function(proto){
}
,SetM
,ShadowM
和SetP
。
您可以使用这4种方法设置您的界面。
此框架还提供了接口的延迟实例化。 (换句话说,在实际首次需要之前,设置代码实际上永远不会运行。)
此框架的限制要求至少支持ShadowP
,Object.keys
和Object.getOwnPropertyDescriptor
。 (换句话说,它适用于最新版本的FireFox,IE,Chrome,Safari,但不适用于Opera)
<强> TestPage2.html:强>
Object.defineProperty
<强> TestPage.html:强>
<!doctype html>
<script src="js.js"></script>
<script>
var human = GetInterface(function(){
},function(proto){
//alert("trace: initing human");
proto.$SetM("Sleep",function(){
alert(this.Name+" is sleeping");
});
proto.$SetP("Name",function(){
return this._name;
},function(value){
this._name=value;
});
});
var female = GetInterface(function(){
},function(proto){
//alert("trace: initing female");
proto.$SetM("Dance",function(){
alert(this.Name+" is dancing");
});
},human);
var male = GetInterface(function(){
},function(proto){
//alert("trace: initing male");
proto.$SetM("Fight",function(){
alert(this.Name+" is fighting");
});
proto.$ShadowP("Name",function(parent_get){
return "Mr. "+parent_get();
},function(parent_set,value){
parent_set(value);
});
},human);
var child = GetInterface(function(){
},function(proto){
//alert("trace: initing child");
proto.$SetM("Play",function(){
alert(this.Name+" is playing");
});
},human);
var adult = GetInterface(function(){
},function(proto){
//alert("trace: initing adult");
proto.$SetM("Work",function(){
alert(this.Name+" is working");
});
},human);
var mammal = GetInterface(function(){
},function(proto){
//alert("trace: initing mammal");
proto.$SetM("DoMammalStuff",function(){
alert("doing mammal stuff");
});
});
var john=new male();
john.Name="john";
john.Sleep();
var mary=new female();
mary.$IsA(child);
mary.$IsA(mammal);
mary.$Setup(function(proto){
proto.$ShadowP("Name",function(parent_get){
return "Miss "+parent_get.call(this);
},function(parent_set,value){
parent_set.call(this,value);
});
});
mary.Name="mary";
mary.Play();
</script>
<强> Js.js:强>
<!doctype html>
<script src="js.js"></script>
<script>
var human_interface = GetInterface(function(){
},function(proto){
alert("trace: initing human");
proto.$SetM("Sleep",function(){
alert(this.Name+" is sleeping");
});
proto.$SetP("Name",function(){
return this._name;
},function(value){
this._name=value;
});
});
var female_interface = GetInterface(function(){
},function(proto){
alert("trace: initing female");
proto.$SetM("Dance",function(){
alert(this.Name+" is dancing");
});
},human_interface);
var male_interface = GetInterface(function(){
},function(proto){
alert("trace: initing male");
proto.$SetM("Fight",function(){
alert(this.Name+" is fighting");
});
},human_interface);
var child_interface = GetInterface(function(){
},function(proto){
alert("trace: initing child");
proto.$SetM("Play",function(){
alert(this.Name+" is playing");
});
},human_interface);
var adult_interface = GetInterface(function(){
},function(proto){
alert("trace: initing adult");
proto.$SetM("Work",function(){
alert(this.Name+" is working");
});
},human_interface);
var mammal_interface = GetInterface(function(){
},function(proto){
alert("trace: initing mammal");
proto.$SetM("DoMammalStuff",function(){
alert("doing mammal stuff");
});
});
var john={};
john.$IsA(adult_interface);
//the above 2 lines are equal to simply doing:
//var john=new adult_interface();
//you can think of it as a shortcut
john.$IsA(mammal_interface);
john.DoMammalStuff();
john.Name="john";
john.Sleep();
var mary=new female_interface();
mary.$IsA(child_interface);
mary.$IsA(mammal_interface);
mary.DoMammalStuff();
mary.Name="mary";
mary.Play();
mary.Dance();
</script>
答案 5 :(得分:0)
ExtJS是JavaScript OO的一个很好的例子。它在JavaScript中实现了一个非常复杂的企业级OO层次结构,它可以提供大量开箱即用的功能。这可能是一个令人生畏的阅读(最后我检查3.X,它是超过1MB的原始,未压缩的JavaScript),但它会给你很多想法。您可以从reviewing the documentation开始,以获得高级视图。
答案 6 :(得分:0)
这是一个显示您正在寻找的OO编程基础的示例。最好的现实世界的例子可能是jQuery。
学习JavaScript时,你必须记住,它实际上更接近于Scheme,而不是它的根或者是C或Java。基本上它是C语法中的Scheme。
几乎从来没有一种情况应该在JavaScript中使用“new”,尤其是在编写API时。似乎添加了“新”运算符,因为JavaScript不确定其原型框架。对于我们大多数开始使用C,C ++和Java等经典语言编程的人来说,这看起来很奇怪,因为“新”通常正是我们所寻找的,因为它很容易翻译。
为什么我不应该使用你问的“新”?好吧,由于“new”的实现,你可能会无意中开始消灭你的全局数据(记住,这些都不是JavaScript中的函数)。如果您碰巧成为这个的牺牲品,那么您将看不到任何错误或通知,但只能看到程序中不可预测的行为。此外,它可能会或可能不会清楚“你”在你的“班级”中实际上是什么。
在不知道问题的情况下消除全局内存主要发生在编写一个打算用“new”调用并且用户不使用“new”的函数时。提示为什么在API中使用它会导致不满意的用户。
面向对象“类”和继承的正确方法是使用JavaScript最强大的属性......对象。
您可以编写一个函数来返回一个对象来建立一个“类”。您放入此对象的任何内容(数字,字符串,方法等)都是您“类”的公共属性。你在函数中编写的任何不在返回的对象内的东西都是私有的。
要从“类”继承,您只需初始化您将返回到“基类”结果的对象,然后扩展其功能。
以下代码部分将展示如何构建具有私有和公共变量的基类,以及do 2级继承。
基础对象
//Base Object
var Animal = function(spec) {
//This is our output object
//Everything provided from 'spec' and
//everything not addded to 'that' will
//be 'private'. Everything added to
//'that' is 'public'.
var that = {};
//Private Methods
function extend(obj1,obj2) {
for(var key in obj2) {
obj1[key] = obj2[key];
}
}
//Private Variables
var defaults = {
name : 'Default Name',
food : 'Default Food',
saying : 'Default Saying',
}
extend(defaults,spec);
//Public Methods
that.name = function() {
return defaults.name;
}
that.eats = function() {
if(typeof(defaults.food) === 'string') {
return defaults.food;
} else if(typeof(defaults.food) === 'object') {
return defaults.food.join(', ');
}
}
that.says = function() {
return defaults.saying;
}
return that;
}
var myAnimal = Animal(); //Create a new instance
alert(myAnimal.name()); //Alerts 'Default Name'
alert(myAnimal.eats()); //Alerts 'Default Food'
alert(myAnimal.says()); //Alerts 'Default Saying'
alert(myAnimal.saying); //Alerts 'undefined'
//alert(myAnimal.extend()); //Has No Method Error
var myAnimal2 = Animal({ //Create a new instance using a spec
name : 'Mike',
food : ['Chicken','Duck'],
saying : 'Rawr',
});
alert(myAnimal2.name()); //Alerts 'Mike'
alert(myAnimal2.eats()); //Alerts 'Chicken, Duck'
alert(myAnimal2.says()); //Alerts 'Rawr'
<强>继承强>
//Inheritance Object
var Mammal = function(spec) {
//Private Methods
//Have to redefine this since
//I decided to use this as an
//example of a private method
function extend(obj1,obj2) {
for(var key in obj2) {
obj1[key] = obj2[key];
}
}
//Private Variables
//New list of defaults
var defaults = {
name : 'Mammal',
attributes : ['fur'],
}
extend(defaults,spec);
//Inherrit from our Animal Object
//Use Mammal defaults
var that = Animal(defaults);
that.attributes = function() {
if(typeof(defaults.attributes) === 'string') {
return defaults.attributes;
} else if(typeof(defaults.attributes) === 'object') {
return defaults.attributes.join(', ');
} else {
return false;
}
}
return that;
}
//Second-Level Inheritance
var Cat = function(spec) {
//Private Methods
//Have to redefine this since
//I decided to use this as an
//example of a private method
function extend(obj1,obj2) {
for(var key in obj2) {
obj1[key] = obj2[key];
}
}
//Private Variables
//New list of defaults
var defaults = {
name : 'Cat',
saying : 'Meow',
food : ['fish','birds','frogs','MeowMix'],
fur_color : 'Default Fur Color',
attributes : ['fur','claws','crazy eyes','long tail'],
}
extend(defaults,spec);
//Inherrit from our Mammal Object
//We use our defaults for the cat
var that = Mammal(defaults);
that.fur_color = function() {
return defaults.fur_color;
}
that.purr = function(n) {
var str = '';
for(var i=0;i<n;i++) {
if(i === 0) {
str = 'p-';
} else if(i === n-1) {
str += 'r';
} else {
str += 'r-';
}
}
return str
};
return that;
}
var myMammal = Mammal();
alert(myMammal.name()); //Alerts Mammal
alert(myMammal.attributes()); //Alerts 'fur'
var myCat = Cat();
alert(myCat.name()); //Alerts Cat
alert(myCat.says()); //Alerts Meow
var toonces = Cat({
name : 'Toonces the Driving Cat',
food : ['Whiskas','ham'],
saying : 'Meeeooooowww',
fur_color : 'Black',
attributes : [
'Can Drive a Car', 'Claws',
'fur','crazy eyes','long tail',
'Steals Hub Caps',
],
});
alert(toonces.name()); //Alerts 'Toonces the Driving Cat'
alert(toonces.says()); //Alerts 'Meeooooowww'
alert(toonces.eats()); //Alerts 'Whiskas, ham'
alert(toonces.fur_color()); //Alerts 'Black'
alert(toonces.attributes()); //Alerts 'Can Drive a Car, Claws,
//fur, crazy eyes, long tail,
// Steals Hub Caps',
alert(toonces.purr(5)); //Alerts 'p-r-r-r-r'
编辑:我被警告我没有使用“原型”对象。我这样做,以避免使用“新”运算符,如上文所述。为了完整起见,我将使用下面的原型对象给出一个例子......
使用原型对象继承
//Building a class to use the prototype object
var Dog = function(spec) {
var that = this;
//Private Methods
//Have to redefine this since
//I decided to use this as an
//example of a private method
function extend(obj1,obj2) {
for(var key in obj2) {
obj1[key] = obj2[key];
}
}
//Private Variables
//New list of defaults
var defaults = {
name : 'Dog',
saying : 'Woof',
food : ['bacon'],
fur_color : 'Default Fur Color',
attributes : ['fur','Barks at Mailman'],
}
//Attach the properties of a Mammal to "self"
this.self = new Mammal(defaults);
//Add a function to get the name
this.getName = function() {
return that.self.name();
}
}
//Extend the prototype
Dog.prototype.growl = "grrrrrrr";
//Make a new dog...HAVE TO CALL NEW HERE OR ELSE BAD THINGS CAN HAPPEN
d= new Dog();
alert(d.growl); //Alerts 'grrrrrrr'
alert(d.getName()); //Alerts 'Dog'
alert(d.self.says()); //Alerts 'Woof'
请随时向我询问这篇文章的任何内容。享受。
答案 7 :(得分:0)
我目前正在使用继承插件模型,该模型尝试将原型OO模式与jQuery插件模式相结合。它在我的回答中详细说明:attaching a class to a jQuery object
注意:请勿提及Class