(开源)JavaScript Prototypical OO的例子

时间:2011-06-30 11:55:02

标签: javascript oop prototypal-inheritance ecmascript-5 prototypal

Bounty编辑:

我正在寻找用原型OO范例编写的代码(想想Self)。不是原型OO和经典OO的混合物。我不想看到通用的OO包装器,而只是使用原型OO技术和原型OO技术。

参考相关问题:

Prototypical OO in JavaScript

在上面的问题中我主要关注

  

可以像这样编写原型OO吗?

     

我们是否需要构造函数和初始化逻辑,有哪些替代方案?

新问题:

在大型开源项目中,基本上有没有 javascript原型 OO的好例子?

澄清:

我必须澄清我对原型OO的意思

  • 没有课程。只有对象。
  • 对类的概念进行仿真,再次只有对象和克隆对象来创建新对象。

进一步澄清原型OO:

JavaScript中的原型OO与经典OO仿真之间的区别是非常灰色的区域。这不是我避免使用经典的OO。我想以学术的方式学习原型OO本身,而不学习经典OO仿真和原型OO的(可能更优化)组合。

这就是为什么我“禁止”课程,只是为了让我能够以纯粹的方式看到这些技术并扩展我自己的OO工具包。

示例:

像jQuery这样的热门示例无法满足第二个标准。 jQuery对象是一个大类仿真。它侧重于从类创建新对象,而不是克隆现有对象。

如果我真的知道使用“pure”原型OO的任何例子,我会告诉你。我相信99%的JavaScript OO受到经典仿真的影响太大。

加分

如果

  • 很好地记录/记录
  • 有单元测试
  • 在github上。

我还将接受有关如何编写超出琐碎的hello world应用程序的原型OO代码的文章/教程和示例。

8 个答案:

答案 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 ToolkitExtJS。值得记住的是,基于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_Binterface_C,您可以创建interface_A:GetInterface(constructor, interface_setup, interface_B, interface_C);

如果interface_A继承interface_Xinterface_X继承interface_Y,那么interface_A将拥有interface_X和{{1}的所有功能有}。

不需要构造函数的接口会将构造函数参数保留为null。 interface_setup是一个如下所示的函数:

interface_Y

proto 参数指向的对象有4种方法:function(proto){ } SetMShadowMSetP

您可以使用这4种方法设置您的界面。

此框架还提供了接口的延迟实例化。 (换句话说,在实际首次需要之前,设置代码实际上永远不会运行。)

此框架的限制要求至少支持ShadowPObject.keysObject.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

一词。