Хороший пример наследования на основе прототипов JavaScript - PullRequest
88 голосов
/ 14 января 2010

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

Ответы [ 10 ]

76 голосов
/ 14 ноября 2012

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

48 голосов
/ 19 января 2010

У Дугласа Крокфорда есть хорошая страница о наследовании JavaScript-прототипа :

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

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

26 голосов
/ 15 апреля 2014
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);
14 голосов
/ 14 января 2010

Я бы посмотрел на YUI и на библиотеку Дина Эдварда Base: http://dean.edwards.name/weblog/2006/03/base/

Для YUI вы можете быстро взглянуть на lang module , esp. метод YAHOO.lang.extend . А затем вы можете просмотреть источник некоторых виджетов или утилит и посмотреть, как они используют этот метод.

5 голосов
/ 14 января 2010

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

Существует также много хороших статей по MSDN, включая Создание расширенных веб-приложений с использованием объектно-ориентированных методов .

4 голосов

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
3 голосов
/ 12 января 2016

Это самый ясный пример, который я нашел из книги Узла Миксу (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 и имеет функциональность, которая люди в основном ищут: цепные конструкторы и возможность вызова методы суперкласса.

2 голосов
/ 21 января 2010

Я предлагаю взглянуть на PrototypeJS 'Class.create:
Строка 83 @ http://prototypejs.org/assets/2009/8/31/prototype.js

1 голос
/ 22 января 2010

Лучшие примеры, которые я видел, есть в «Дугласе Крокфорде» «JavaScript: хорошие детали» . Это определенно стоит купить, чтобы помочь вам получить сбалансированный взгляд на язык.

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

0 голосов
/ 28 мая 2015

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

...