Правильное наследование прототипа
Поэтому я действительно просмотрел весь Интернет и нашел много разных способов настройки наследования прототипов в javascript.
Некоторые из них используют call()
,
Некоторые из них используют этот синтаксис: var rabbit.prototype = new Animal
,
Некоторые из них меняют конструктор после изменения прототипа, некоторые - нет.
Некоторые устанавливают небольшую функцию, которая помогает установить наследование.
Может ли кто-нибудь пролить свет на это? Есть много постов по этому поводу, но хорошие из них 2+ года, и они вызвали у меня много путаницы.
Я хотел бы знать, раз и навсегда, как на самом деле ПРАВИЛЬНО установить наследование прототипа в javascript.
Даже лучше, если это просто!
3 ответа
Реализовав несколько разных способов наследования javascript, я решил, что в конечном итоге я разработал игровой движок javascript для браузера rpg:
Базовый класс игрока:
function Player(name, type, gender, experience, avatar){
this.name = name;
this.type = type;
this.gender = gender;
this.experience = experience;
this.avatar = avatar;
this.stats ={//getter, setter}
//lots more code
}
Добавление метода в класс игрока
Player.prototype.decrease_life = function(decrement){}
//note that the keyword this in the decrease_life function will
//refer to the player that the method is called on.
Теперь о наследовании класса игрока:
function Mage(name, type, gender, exp, avatar){
Player.apply(this, [name,type,gender,exp,avatar]);
//apply allows you to specify what the keyword
//this refers to in the Player super class.
}
Mage.prototype = new Player;
И, наконец, мы создаем игрока:
current_player = new Mage(name,type,gender,0,avatar);
Что позволяет нам сделать это сейчас:
current_player.decrease_life(20); //The mage loses 20 life!
Или сделать это:
current_player.stats.get();
//returns the mages stats, it does that because we used apply, and
//this.stats in the player class is referring to our mage now
Как уже упоминали другие, не существует наилучшей практики наследования JavaScript. Я обнаружил, что вышесказанное наиболее близко имитирует, как можно ожидать, что наследование будет работать в Java или C++, которые имеют более типичные структуры наследования.
The important thing about prototypal inheritance is that you're inheriting from objects and not classes. So the idea is that tomsBankAccount
inherits off bankAccount
, which would probably be done via instantation in a class-based language. When you want an 'instance' of a 'class', that'd be done via an object implementing the common features and it being the prototype of any object that wants those features.
Here's a concrete example that is both untested and wrong. But it's wrong for a reason I'll get to.
var bankAccount = {
withdraw: function(amount) { this.balance -= amount },
deposit: function(amount) { this.balance += amount }
}
var account1 = {
balance: 50
}
account1.prototype = bankAccount
account1.deposit(100)
alert(account1.balance) // Displays 150
So an object has its own stuff like the balance
field, yet delegates common functionality off to the bankAccount object. So the equivalent of inheritance would be to decide on a common-case object and prototype from it a lot.
Actually, the above code is broken. JavaScript doesn't let you just prototype off any old object, or at least older JavaScript doesn't. It forces prototyping to be done only from objects that are returned from constructor functions
, which have a special syntax in JavaScript when you call them, namely the new
ключевое слово.
You may have seen code that looks something like this (again, untested):
function BankAccount(balance) {
this.balance = balance
this.withdraw = function(amount) { this.balance -= amount }
this.deposit = function(amount) { this.balance += amount }
}
var account1 = new BankAccount(50)
account1.deposit(100)
alert(account1.balance) // Same result as last time
You'll also note they start with a capital letter. Вот и все, в основном. this
is odd -- it carries around a reference to the object the function is contained within. So the object carrying the deposit
method also carries around the balance
attribute, which is why we can access balance
с помощью this
from the methods.
But the above code isn't idiomatic JavaScript. There are things to remember once you've got the hang of it. For example, the above reassigns new methods on every object construction which is obviously not quick. This is resolved by assigning methods to a function constructor's prototype. There's also the ability to 'privatise' attributes via JavaScript's closures.
You may suspect that JavaScript's Java-like notation for doing objects is completely misleading given that its so different underneath... and I'd agree.
Не существует действительно единого "лучшего способа" сделать это. "Хорошие ответы" от 2-х лет по-прежнему актуальны (если они хороши, то есть, и я лично откажусь от всего, что пытается эмулировать класс), так как javascript (EcmaScript) довольно старый (более десяти лет).
call
используется, чтобы передать то, что this
является.
Что касается наследования прототипа: мне очень понравился 'Crockford on Javascript' из театра YUI. Вы хотели бы посмотреть " Акт III: функционировать в конечном итоге"
Самый простой способ установить прототип:objectIdentifier.prototype.methodIdentifier = function(args){ /* your code*/ }
Существует множество различных "конструктивных паттернов", и большинство из них используются в конкретной конкретной ситуации. Как правило, стоит подумать о том, что собирается делать часть кода и как часто вы ее используете. Вы также хотели бы взглянуть на некоторые популярные библиотеки, такие как JQuery, и взглянуть на их шаблоны.
Надеюсь это поможет!