Я программировал на языках ООП более 10 лет, но сейчас изучаю JavaScript, и впервые столкнулся с наследованием на основе прототипов. Я стараюсь учиться быстрее всего, изучая хороший код. Каков хорошо написанный пример приложения (или библиотеки) JavaScript, которое правильно использует прототипное наследование? И не могли бы вы описать (вкратце), как / где используется прототипное наследование, чтобы я знал, с чего начать?
javascript
prototypal-inheritance
Алекс Рейснер
источник
источник
Ответы:
У Дугласа Крокфорда есть хорошая страница о наследовании прототипов JavaScript :
Работы Дина Эдварда Base.js , Класс Mootools или Простое наследование Джона Ресига - это способы реализовать классическое наследование в JavaScript.
источник
newObj = Object.create(oldObj);
если вы хотите, чтобы это было бесплатно? В противном случае заменитьoldObj
на объект прототипа функции конструктора должна работать?Как уже упоминалось, фильмы Дугласа Крокфорда дают хорошее объяснение того, почему, и как. Но чтобы выразить это в нескольких строках JavaScript:
// Declaring our Animal object var Animal = function () { this.name = 'unknown'; this.getName = function () { return this.name; } return this; }; // Declaring our Dog object var Dog = function () { // A private variable here var private = 42; // overriding the name this.name = "Bello"; // Implementing ".bark()" this.bark = function () { return 'MEOW'; } return this; }; // Dog extends animal Dog.prototype = new Animal(); // -- Done declaring -- // Creating an instance of Dog. var dog = new Dog(); // Proving our case console.log( "Is dog an instance of Dog? ", dog instanceof Dog, "\n", "Is dog an instance of Animal? ", dog instanceof Animal, "\n", dog.bark() +"\n", // Should be: "MEOW" dog.getName() +"\n", // Should be: "Bello" dog.private +"\n" // Should be: 'undefined' );
Однако проблема с этим подходом заключается в том, что он будет воссоздавать объект каждый раз, когда вы его создаете. Другой подход - объявить ваши объекты в стеке прототипов, например:
// Defining test one, prototypal var testOne = function () {}; testOne.prototype = (function () { var me = {}, privateVariable = 42; me.someMethod = function () { return privateVariable; }; me.publicVariable = "foo bar"; me.anotherMethod = function () { return this.publicVariable; }; return me; }()); // Defining test two, function var testTwo = function() { var me = {}, privateVariable = 42; me.someMethod = function () { return privateVariable; }; me.publicVariable = "foo bar"; me.anotherMethod = function () { return this.publicVariable; }; return me; }; // Proving that both techniques are functionally identical var resultTestOne = new testOne(), resultTestTwo = new testTwo(); console.log( resultTestOne.someMethod(), // Should print 42 resultTestOne.publicVariable // Should print "foo bar" ); console.log( resultTestTwo.someMethod(), // Should print 42 resultTestTwo.publicVariable // Should print "foo bar" ); // Performance benchmark start var stop, start, loopCount = 1000000; // Running testOne start = (new Date()).getTime(); for (var i = loopCount; i>0; i--) { new testOne(); } stop = (new Date()).getTime(); console.log('Test one took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds'); // Running testTwo start = (new Date()).getTime(); for (var i = loopCount; i>0; i--) { new testTwo(); } stop = (new Date()).getTime(); console.log('Test two took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');
Когда дело доходит до самоанализа, есть небольшая обратная сторона. Сброс testOne приведет к получению менее полезной информации. Кроме того, частное свойство privateVariable в testOne используется во всех экземплярах, как это было полезно упомянуто в ответах shesek.
источник
privateVariable
- это просто переменная в области действия IIFE , которая используется во всех экземплярах, поэтому вам не следует хранить в ней данные, относящиеся к конкретному экземпляру. (на testTwo это зависит от экземпляра, так как каждый вызов testTwo () создает новую область для каждого экземпляра)Dog.prototype
. Поэтому вместо использованияthis.bark = function () {...}
мы можем действоватьDot.prototype.bark = function () {...}
внеDog
функции. (Подробнее см. В этом ответе )function Shape(x, y) { this.x = x; this.y = y; } // 1. Explicitly call base (Shape) constructor from subclass (Circle) constructor passing this as the explicit receiver function Circle(x, y, r) { Shape.call(this, x, y); this.r = r; } // 2. Use Object.create to construct the subclass prototype object to avoid calling the base constructor Circle.prototype = Object.create(Shape.prototype);
источник
Я бы взглянул на YUI и
Base
библиотеку Дина Эдварда : http://dean.edwards.name/weblog/2006/03/base/Для YUI вы можете быстро взглянуть на модуль lang , особенно. YAHOO.lang.extend метод. А затем вы можете просмотреть источник некоторых виджетов или утилит и посмотреть, как они используют этот метод.
источник
lang
него частично не работает. Кто-нибудь хочет исправить это для YUI 3?Также существует библиотека Microsoft ASP.NET Ajax, http://www.asp.net/ajax/ .
Также есть много хороших статей MSDN, в том числе « Создание расширенных веб-приложений с помощью объектно-ориентированных методов» .
источник
Это самый яркий пример, который я нашел, из книги Mixu Node ( http://book.mixu.net/node/ch6.html ):
источник
ES6
class
иextends
ES6
class
иextends
являются всего лишь синтаксическим сахаром для ранее возможных манипуляций с цепочкой прототипов и, возможно, наиболее канонической установкой.Сначала узнайте больше о цепочке прототипов и
.
поиске свойств по адресу: https://stackoverflow.com/a/23877420/895245Теперь давайте разберемся, что происходит:
class C { constructor(i) { this.i = i } inc() { return this.i + 1 } } class D extends C { constructor(i) { super(i) } inc2() { return this.i + 2 } }
// Inheritance syntax works as expected. (new C(1)).inc() === 2 (new D(1)).inc() === 2 (new D(1)).inc2() === 3
// "Classes" are just function objects. C.constructor === Function C.__proto__ === Function.prototype D.constructor === Function // D is a function "indirectly" through the chain. D.__proto__ === C D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class // lookups will work as expected var d = new D(1) d.__proto__ === D.prototype D.prototype.__proto__ === C.prototype // This is what `d.inc` actually does. d.__proto__.__proto__.inc === C.prototype.inc
// Class variables // No ES6 syntax sugar apparently: // /programming/22528967/es6-class-variable-alternatives C.c = 1 C.c === 1 // Because `D.__proto__ === C`. D.c === 1 // Nothing makes this work. d.c === undefined
Упрощенная диаграмма без всех предопределенных объектов:
__proto__ (C)<---------------(D) (d) | | | | | | | | | |prototype |prototype |__proto__ | | | | | | | | | | | +---------+ | | | | | | | | | | v v |__proto__ (D.prototype) | | | | | | | | |__proto__ | | | | | | | | +--------------+ | | | | | | | v v | (C.prototype)--->(inc) | v Function.prototype
источник
Предлагаю посмотреть на PrototypeJS Class.create:
Line 83 @ http://prototypejs.org/assets/2009/8/31/prototype.js
источник
Лучшие примеры, которые я видел, находятся в книге Дугласа Крокфорда « JavaScript: хорошие части». . Это определенно стоит покупать, чтобы помочь вам получить сбалансированное представление о языке.
Дуглас Крокфорд отвечает за формат JSON и работает в Yahoo как гуру JavaScript.
источник
Существует фрагмент кода JavaScript "Наследование на основе прототипов" с реализациями для конкретных версий ECMAScript. Он автоматически выберет, что использовать из реализаций ES6, ES5 и ES3 в соответствии с текущим временем выполнения.
источник
Добавление примера наследования на основе прототипа в Javascript.
// Animal Class function Animal (name, energy) { this.name = name; this.energy = energy; } Animal.prototype.eat = function (amount) { console.log(this.name, "eating. Energy level: ", this.energy); this.energy += amount; console.log(this.name, "completed eating. Energy level: ", this.energy); } Animal.prototype.sleep = function (length) { console.log(this.name, "sleeping. Energy level: ", this.energy); this.energy -= 1; console.log(this.name, "completed sleeping. Energy level: ", this.energy); } Animal.prototype.play = function (length) { console.log(this.name, " playing. Energy level: ", this.energy); this.energy -= length; console.log(this.name, "completed playing. Energy level: ", this.energy); } // Dog Class function Dog (name, energy, breed) { Animal.call(this, name, energy); this.breed = breed; } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.bark = function () { console.log(this.name, "barking. Energy level: ", this.energy); this.energy -= 1; console.log(this.name, "done barking. Energy level: ", this.energy); } Dog.prototype.showBreed = function () { console.log(this.name,"'s breed is ", this.breed); } // Cat Class function Cat (name, energy, male) { Animal.call(this, name, energy); this.male = male; } Cat.prototype = Object.create(Animal.prototype); Cat.prototype.constructor = Cat; Cat.prototype.meow = function () { console.log(this.name, "meowing. Energy level: ", this.energy); this.energy -= 1; console.log(this.name, "done meowing. Energy level: ", this.energy); } Cat.prototype.showGender = function () { if (this.male) { console.log(this.name, "is male."); } else { console.log(this.name, "is female."); } } // Instances const charlie = new Dog("Charlie", 10, "Labrador"); charlie.bark(); charlie.showBreed(); const penny = new Cat("Penny", 8, false); penny.meow(); penny.showGender();
ES6 использует гораздо более простую реализацию наследования с использованием конструктора и ключевых слов super.
источник