(Открытый исходный код) Примеры JavaScript Prototypical OO

Щедрость Править:

Я ищу код, написанный в чисто прототипной ОО-парадигме (подумайте о себе). Не смесь прототипного ОО и классического ОО. Я не хочу видеть универсальные OO-оболочки, а просто использование прототипных OO-технологий и только прототипных OO-методов.

Справочный вопрос:

Прототип ОО в JavaScript

В вышеупомянутом вопросе я в основном сосредоточился на

Можете написать прототип ОО, как это?

Нужны ли нам конструкторы и логика инициализации? Какие есть альтернативы?

Новый вопрос:

В принципе, есть ли хорошие примеры прототипов ОО в javascript в больших проектах с открытым исходным кодом?

Разъяснение:

Я должен уточнить, что я имею в виду с прототипом ОО:

  • Там нет классов. Есть только объекты.
  • Существует нулевая эмуляция понятий классов, опять же есть только объекты и объекты клонирования для создания новых объектов.

Дальнейшее уточнение прототипа ОО:

Разница между прототипом ОО в JavaScript и классической эмуляцией ОО заключается в очень серой области. Дело не в том, что я ценю избегать классического ОО. Я хочу изучать прототипные ОО в академической манере самостоятельно, не изучая (возможно, более оптимальную) комбинацию классической эмуляции ОО и прототипных ОО.

Вот почему я "запрещаю" классы, просто чтобы я мог видеть эти техники в чистом виде и расширять свой собственный набор инструментов OO.

Примеры:

Популярные примеры, такие как jQuery, не соответствуют вторым критериям. jQuery объект - это одна большая эмуляция класса. Он фокусируется на создании новых объектов из класса, а не на клонировании существующих объектов.

Если бы я действительно знал какой-либо пример использования "чистого" прототипного ОО, я бы показал вам. Я считаю, что 99% JavaScript OO слишком сильно подвержены классической эмуляции.

Бонусные очки

Если

  • Это хорошо документировано / задокументировано
  • Имеет юнит-тесты
  • Находится на github.

Я также приму статьи / учебные пособия и примеры о том, как написать прототипный ОО-код, выходящий за рамки вашего простого приложения hello world.

8 ответов

Решение

Вы не найдете это.

Некоторое время назад я искал что-то подобное, и вот что я нашел: Self Paper Organizing Programs Без классов (посмотрите на Citeseer для PDF-версии.) В этой статье обсуждаются лучшие практики для Self, оригинального языка-прототипа, и наилучшей практикой является использование "идиомы объекта признаков", которая заключается в том, что ваши объекты наследуются от "объектов признаков", которые содержат только методы и не содержат данных, специфичных для объекта. Другими словами, объект, который подозрительно похож на класс.

Даже оригинальный язык прототипа эмулирует классы.

Вы смотрели на OMeta/JS? OMeta - это экспериментальный язык сопоставления образцов, основанный на Smalltalk и Self. OMeta/JS - это реализация в javascript, использующая прототип OO.

Это хорошо прокомментировано и задокументировано со многими примерами. Это также на Github.

http://tinlizzie.org/ometa-js/

https://github.com/alexwarth/ometa-js

Редактировать: OMeta является результатом докторской диссертации Александра Варта.

Вероятно, JSLint (Крокфорд - сторонник прототипического наследования, но я не прочесывал каждый его дюйм). Он также выглядит более функциональным, чем объектно-ориентированный, но тогда я ожидаю, что в общем случае это происходит с кодом, который действительно охватывает прототипное наследование.

В моих рамках все является объектом или "интерфейсом".

Интерфейсы определяют общие функции (методы / 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 а также interface_Y есть.

Интерфейсы, которые не требуют конструктора, оставят аргумент конструктора как ноль. Interface_setup - это функция, которая выглядит следующим образом:

function(proto){
}

Объект, на который указывает аргумент прото, имеет 4 метода: SetM, ShadowM, SetP, а также ShadowP,

Вы используете эти 4 метода для настройки вашего интерфейса.

Эта структура также обеспечивает ленивую реализацию интерфейсов. (другими словами, код установки никогда не будет запущен до тех пор, пока он в действительности не понадобится).

Ограничения этой структуры требуют как минимум поддержки Object.keys, Object.getOwnPropertyDescriptor а также Object.defineProperty, (Другими словами, он работает в последних версиях FireFox, IE, Chrome, Safari, но не Opera)

TestPage2.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>

TestPage.html:

 <!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>

Js.js:

"use strict";
var GetInterface;
(function(){
    //================================================================================//
    //(constructor:Function, setup:Function?, parent_interfaces:Function..):Function
    GetInterface = function (constructor, setup) {
        var parent_classes = GetParray(arguments, 2);
        var output = function () {
            output.$Init();
            for (var x = parent_classes.length - 1; x >= 0; --x) {
                parent_classes[x](this);
            }
            if(constructor===null){
                constructor.apply(this, arguments);
            }
        };
        output.$Init = Mize(function () {
            var output_proto = output.prototype;
            parent_classes.forEach(function (parent_class) {
                parent_class.$Init();
                Infect(output_proto, parent_class.prototype);
            });
            init_proto(output_proto,setup);
            if(setup!==undefined){
                setup(output_proto);
            }
        });
        return output;
    };
    var init_proto=function(proto){
        $defineProperty(proto, "$SetM", { value: set_m, writable: true, configurable: true });
        $defineProperty(proto, "$ShadowM", { value: shadow_m, writable: true, configurable: true });
        $defineProperty(proto, "$SetP", { value: set_p, writable: true, configurable: true });
        $defineProperty(proto, "$ShadowP", { value: shadow_p, writable: true, configurable: true });
    };
    var set_m = function (method_name, method) {
        this[method_name] = method;
    };
    var set_p = function (property_name, getter, setter) {
        $defineProperty(this, property_name, { get: getter, set: setter, enumerable: true, configurable: true });
    };
    var shadow_m = function (method_name, supplied_method) {
        var old_method = this[method_name];
        this[method_name] = function () {
            var args = GetParray(arguments);
            args.unshift(old_method.bind(this));
            supplied_method.apply(this, args);
        };
    };
    var shadow_p = function (property_name, getter, setter) {
        var old_descriptor = $getOwnPropertyDescriptor(this, property_name);
        var old_get = old_descriptor.get;
        var old_set = old_descriptor.set;
        $defineProperty(this, property_name, { get: function () {
            return getter.call(this, old_get.bind(this));
        }, set: function (value) {
            setter.call(this, old_set.bind(this), value);
        }, enumerable: true, configurable: true
        });
    };
    var $slice=Array.prototype.slice;
    var $defineProperty=Object.defineProperty;
    var $getOwnPropertyDescriptor=Object.getOwnPropertyDescriptor;
    if($defineProperty===undefined){
        throw "Object.defineProperty, Object.getOwnPropertyDescriptor, Object.keys are required";
    }
    //================================================================================//
    //(victim:Object, disease:Object):void
    var Infect=function (victim, disease, excludes) {
        var keys=Object.keys(disease);
        if(excludes!==undefined){
            excludes.forEach(function(exclude){
                ForEach(keys,function(key,x){
                    if(key===exclude){
                        keys.splice(x,1);
                        return false;
                    }
                });
            });
        }
        keys.forEach(function(key){
            $defineProperty(victim, key, $getOwnPropertyDescriptor(disease, key));
        });
    };
    //================================================================================//
    //(args:Object # arguments object #, start_index:int?):Array
    var GetParray = function (args, start_index) {
        if (start_index === undefined) {
            start_index = 0;
        }
        return $slice.call(args, start_index);
    };
    //================================================================================//
    //(array:Array, f:Function(item:Object|null, index:pint):boolean?):Object
    var ForEach=function(array,f){
        for (var x = 0, xx = array.length, last_index=xx-1; x < xx; ++x) {
            var result = f(array[x], x, last_index);
            if (result !== undefined) {
                return result;
            }
        }
    };
    //================================================================================//
    //provides memoization.
    //(f:Function, arity_fixed:boolean?true):Function
    //caching is done according to the inputs. the results of calling function(undefined) and function() are cached differently.
    //if arity is fixed, optimizations can be done
    var Mize=function(f, arity_fixed) {
        if (arity_fixed === undefined) {
            arity_fixed = true;
        }
        var used; //for 0 arg
        var result; //for 0 arg
        var results; //for >0 args
        var used_params; //for 1 arg
        var used_param_sets; //for >1 args
        var f_length = f.length;
        var use_generic = !arity_fixed || f_length > 3;
        if (use_generic) { //if `f_length` <= 3, it will be optimized (i.e. not using generic function)
            results = [];
            used_param_sets = [];
            return function () {
                var params = GetParray(arguments);
                var result_found = false;
                var result = ForEach(used_param_sets,function (used_param_set, x) {
                    if (used_param_set.length === params.length) {
                        var params_match = true;
                        ForEach(params,function (param, y) {
                            if (used_param_set[y] !== param) {
                                params_match = false;
                                return false;
                            }
                        });
                        if (params_match) {
                            result_found = true;
                            return results[x];
                        }
                    }
                });
                if (!result_found) {
                    used_param_sets.push(params);
                    result = f.apply(null, params);
                    results.push(result);
                }
                return result;
            };
        }
        if (f_length === 0) {
            used = false;
            return function () {
                if (!used) {
                    result = f();
                    used = true;
                }
                return result;
            };
        }
        if (f_length === 1) {
            used_params = [];
        } else {
            used_param_sets = [];
        }
        results = [];
        switch (f_length) {
            case 1:
                return function (arg) {
                    var result_found = false;
                    var result = ForEach(used_params,function (used_param, x) {
                        if (arg === used_param) {
                            result_found = true;
                            return results[x];
                        }
                    });
                    if (!result_found) {
                        used_params.push(arg);
                        result = f(arg);
                        results.push(result);
                    }
                    return result;
                };
                break;
            case 2:
                return function (arg1, arg2) {
                    var result_found = false;
                    var result = ForEach(used_param_sets,function (used_param_set, x) {
                        if (arg1 === used_param_set[0] && arg2 === used_param_set[1]) {
                            result_found = true;
                            return results[x];
                        }
                    });
                    if (!result_found) {
                        used_param_sets.push([arg1, arg2]);
                        result = f(arg1, arg2);
                        results.push(result);
                    }
                    return result;
                };
                break;
            case 3:
                return function (arg1, arg2, arg3) {
                    var result_found = false;
                    var result = ForEach(used_param_sets,function (used_param_set, x) {
                        if (arg1 === used_param_set[0] && arg2 === used_param_set[1] && arg3 === used_param_set[2]) {
                            result_found = true;
                            return results[x];
                        }
                    });
                    if (!result_found) {
                        used_param_sets.push([arg1, arg2, arg3]);
                        result = f(arg1, arg2, arg3);
                        results.push(result);
                    }
                    return result;
                };
                break;
            default:
                throw "Invalid `f_length`: " + f_length;
        }
    };
    //================================================================================//
    Object.prototype.$Setup=function(setup){
        setup(Object.getPrototypeOf(this));
    };
    //================================================================================//
    Object.prototype.$IsA=function(_interface){
        var excludes=GetParray(arguments,1);
        if(this.$SetM===undefined){
            this.$SetM=set_m;
            this.$SetP=set_p;
            this.$ShadowM=shadow_m;
            this.$ShadowP=shadow_p;
        }
        _interface.$Init();
        /*var this_proto={};
        init_proto(this_proto);
        Infect(this_proto,Object.getPrototypeOf(this));
        this.__proto__=this_proto;*/
        Infect(Object.getPrototypeOf(this),_interface.prototype,excludes);
    };
    //================================================================================//
})();

Я не совсем уверен, что вы ищете, но мой текущий фреймворк позволяет программировать в ОО-стиле следующим образом:

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();

Я не пытаюсь подражать классическому ОО здесь. Я пытаюсь найти удобный и полезный способ декларировать наследование, миксины, интерфейсы и общие концепции ОО, чтобы разработчику было легко писать такой ОО-код. Это также дает мне возможность завершить мой компонент автозагрузки, так что вы больше не заботитесь о зависимостях, и вы можете создавать собственные сборки и получать удовольствие от более быстрой разработки благодаря тому, что нет необходимости загружать 100 скриптов на каждую загрузку страницы.

Если вы хотите изучить прототипные концепции ОО, я думаю, что вы должны написать какую-то систему наследования. Взгляните на Dojo Toolkit или ExtJS. Хорошо помнить, что системы на основе прототипов крутятся и деформируются, они более мощные, чем ОО-языки на основе классов. На мой взгляд, нет единственно правильного способа написания прототипа кода.

Боюсь, однако, что большинство, если не все системы наследования могут выглядеть так, будто они эмулируют классическую ОО. На мой взгляд, у меня фреймворка нет, но тогда это даже не закончено.

В настоящее время я использую модель плагина наследования, которая пытается объединить прототип шаблона OO с шаблоном плагина jQuery. Подробно опубликовано в моем ответе здесь: присоединение класса к объекту jQuery

Примечание: не отворачивайтесь от упоминания слова Class

Вот пример, показывающий основы программирования ОО, которые вы ищете. Вероятно, лучшим примером из реальной жизни будет JQuery.

При изучении JavaScript вы должны иметь в виду, что он на самом деле ближе к Scheme, чем C или Java в его корнях. По сути, это схема в синтаксисе Си.

Практически никогда не следует использовать "новое" в JavaScript, особенно если вы пишете API. Похоже, что оператор "new" был добавлен, потому что JavaScript не был уверен в своей прототипной структуре. Для большинства из нас, кто начинает программирование с классических языков, таких как C, C++ и Java, это кажется странным, поскольку "новое", как правило, именно то, что мы ищем, потому что оно легко переводится.

Почему я не должен использовать "новый", спросите вы? Что ж, благодаря реализации "new", вы можете непреднамеренно начать уничтожение ваших глобальных данных (помните, что все это не входит в функцию JavaScript). Если вам случится стать жертвой этого, вы не увидите никаких ошибок или уведомлений, а увидите только непредсказуемое поведение в вашей программе. Кроме того, может быть или не ясно, с чем на самом деле связано "это" в вашем "классе".

Стирание вашей глобальной памяти, не зная об этом, проблема в основном возникает, когда вы пишете функцию, которая предназначена для вызова с "новым", а пользователь не использует "новый". Подсказки, почему использование его в API может привести к несчастью пользователей.

Правильный способ объектно-ориентированных "классов" и наследования - использовать самый мощный атрибут JavaScript - объект.

Вы можете написать функцию для возврата объекта, чтобы установить "класс". Все, что вы помещаете в этот объект (числа, строки, методы и т. Д.), Является общедоступными свойствами вашего "класса". Все, что вы пишете в своей функции, но не внутри возвращаемого объекта, является частным.

Чтобы унаследовать от вашего "класса", вы можете просто инициализировать ваш объект, который вы будете возвращать в ваш "базовый класс", а затем расширить его функциональность.

В следующих разделах кода будет показано, как создать базовый класс с закрытыми и открытыми переменными, а также с двумя уровнями наследования.

Базовый объект

//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'

РЕДАКТИРОВАТЬ: меня предупредили, что я не использовал объект-прототип. Я сделал это, чтобы не пришлось использовать оператор "new", как упомянуто в тексте выше. Для полноты я приведу пример с использованием объекта-прототипа ниже...

Наследование с прототипом объекта

//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'

Пожалуйста, не стесняйтесь спрашивать меня о любом из этого поста. Наслаждаться.

ExtJS является отличным примером JavaScript OO. Он реализует действительно сложную OO-иерархию корпоративного уровня в JavaScript, которая делает многое из коробки. Это может быть сложное чтение (в последний раз я проверял в 3.X, это было более 1 МБ необработанного, несжатого JavaScript), но это дало бы вам много идей. Вы можете начать с просмотра документации, чтобы получить общее представление.

Другие вопросы по тегам