Хороший пример наследования на основе прототипов JavaScript


я программирую на языках ООП уже более 10 лет, но сейчас я изучаю JavaScript, и это первый раз, когда я столкнулся с наследованием на основе прототипов. Я склонен учиться быстрее, изучая хороший код. Что такое хорошо написанный пример приложения JavaScript (или библиотеки), которое правильно использует прототипное наследование? И можете ли вы описать (кратко), как/где используется прототипное наследование, чтобы я знал, с чего начать чтение?

10 84

10 ответов:

Дуглас Крокфорд имеет хорошую страницу на Прототипное Наследование JavaScript:

пять лет назад я писал Классическое Наследование в JavaScript. Он показал, что JavaScript является бесклассовым прототипным языком и что он обладает достаточной выразительной силой для моделирования классической системы. С тех пор мой стиль программирования эволюционировал, как и у любого хорошего программиста. Я научился полностью принимать прототипизм и освободился от него. границы классической модели.

Дин Эдвард базы.js, класс Mootools или простое наследство Джона Ресига работы-это способы сделать классическое наследование в 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');

есть небольшой недостаток, когда дело доходит до самоанализа. Сбрасывать Тестоне, приведет в меньше полезная информация. Также частная собственность " privateVariable "в" testOne " является общей во всех случаях, также услужливо упоминается в ответах shesek.

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

Я хотел бы взглянуть на Юи, и у декана Эдварда Base библиотека:http://dean.edwards.name/weblog/2006/03/base/

для Юи вы можете быстро взглянуть на модуль lang, esp. отвратительное существо.ленг.расширьте метод. А затем вы можете просмотреть источник некоторых виджетов или утилит и посмотреть, как они используют этот метод.

есть также Microsoft ASP.NET библиотека Ajax,http://www.asp.net/ajax/.

есть много хороших статей MSDN вокруг, а также, в том числе Создание Продвинутых Веб-Приложений С Объектно-Ориентированными Методами.

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:
// https://stackoverflow.com/questions/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

Это самый яркий пример, который я нашел, из книги узлов Mixu (http://book.mixu.net/node/ch6.html):

Я предпочитаю композицию наследованию:

композиция-функциональность объекта состоит из совокупности различные классы, содержащие экземпляры других объектов. Наследование-функциональность объекта состоит из его собственного функциональность плюс функциональность от своих родительских классов. Если вы должны иметь наследование, используйте простой старый JS

Если вы должны реализовать наследование, по крайней мере, избегайте использования еще одного нестандартная реализация / магическая функция. Вот как вы можете реализовать разумное факсимиле наследования в чистом ES3 (как долго поскольку вы следуете правилу никогда не определять свойства на прототипах):

function Animal(name) {
  this.name = name;
};
Animal.prototype.move = function(meters) {
  console.log(this.name+" moved "+meters+"m.");
};

function Snake() {
  Animal.apply(this, Array.prototype.slice.call(arguments));
};
Snake.prototype = new Animal();
Snake.prototype.move = function() {
  console.log("Slithering...");
  Animal.prototype.move.call(this, 5);
};

var sam = new Snake("Sammy the Python");
sam.move();

Это не то же самое, что классическое наследование, но это стандартный, понятный Javascript и обладает той функциональностью, которая люди в основном ищут: цепные конструкторы и возможность вызова методы суперкласса.

Я предлагаю посмотреть на класс PrototypeJS.создать:
Строка 83 @ http://prototypejs.org/assets/2009/8/31/prototype.js

лучших примеров, которые я видел в JavaScript: Хорошие Части. Это определенно стоит купить, чтобы помочь вам получить сбалансированный взгляд на язык.

Дуглас Крокфорд отвечает за формат JSON и работает в Yahoo в качестве гуру JavaScript.

есть фрагмент наследование на основе прототипов JavaScript с конкретными реализациями версии ECMAScript. Он автоматически выберет, что использовать между реализациями ES6, ES5 и ES3 в соответствии с текущим временем выполнения.