问题
So I really looked all over the internet and found many different ways on setting prototypal inheritance in javascript.
Some of them use call()
.
Some of them use this sytnax: var rabbit.prototype = new Animal
.
Some of them change the constructor after changing the prototype, some do not.
Some set a little function which helps set inheritance.
Could someone please shed some light on this? There are many posts about this but the good ones are 2+ years old and they've caused a great deal of confusion in my mind.
I would like to know, once and for all, how to actually PROPERLY set prototypal inheritance in javascript.
Even better if it's simple!
回答1:
Having implemented several different ways of javascript inheritance this is ultimately what I went with when I wanted to construct a javascript game engine for a browser rpg:
The player base class:
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
}
Adding a method to the player class
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.
Now the inheritance of the player class:
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;
And finally we create a player:
current_player = new Mage(name,type,gender,0,avatar);
Which allows us to do this now:
current_player.decrease_life(20); //The mage loses 20 life!
Or do this:
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
As others have mentioned there is no best practice in javascript inheritance. I have found the above most closely imitates how you would expect inheritance to work in Java or C++, which have more typical inheritance structures.
回答2:
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
keyword.
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. That's it, basically. 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
via 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.
回答3:
There is no truly uniform 'best way' to do it. 'Good answers' from 2 years old are still relevant (if they are good that is and I personally would discard anything that tries to emulate a class), since javascript (EcmaScript) is quite old (over a decade).
call
is used to pass what this
is.
As to prototypal inheritance: I really liked 'crockford on javascript' from the YUI Theater. You'd want to watch 'Act III: function the ultimate'
The simplest way to set the prototype is:objectIdentifier.prototype.methodIdentifier = function(args){ /* your code*/ }
There are many different 'construct-patterns' and most of them have their use in a specific given situation. It is generally a good idea to think about what a piece of code is going to do and how often you use it. You'd also want to look at some popular library's like JQuery and look at their patterns.
Hope this helps!
来源:https://stackoverflow.com/questions/16020577/proper-prototypal-inheritance