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

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

10 ответов


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

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

Дин Эдвард базы.js, класс Mootools или простое наследство Джона Ресига работы-это способы сделать классическое наследование в JavaScript.


Как уже упоминалось, фильмы Дугласа Крокфорда дают хорошее объяснение о том, почему, и оно охватывает как. Но чтобы поместить его в пару строк 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.


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, esp. отвратительное существо.ленг.расширьте метод. А затем вы можете просмотреть источник некоторых виджетов или утилит и посмотреть, как они используют этот метод.


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

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


ES6 class и extends

ЕС6 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):

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

Composition-функциональность объекта состоит из совокупности различные классы, содержащие экземпляры других объектов. Наследование-функциональность объекта состоит из его собственного функциональность плюс функциональность из родительских классов. Если нужно ... иметь наследование, используйте простой старый 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. Он будет автоматически выбрать между ЕС6, в ES5 ES3 реализации по данным текущего времени.