It sets the prototype and then resets the correct constructor. As a bonus, it adds the empty class F into the prototype chain in order to prevent a new (and possible large) instance of the superclass from having to be instantiated. This is also beneficial in situations where the superclass’s constructor has side effects or does something that is computationally intensive. Since the object that gets instantiated for the prototype is usually just a throwaway instance, you don’t want to create it unnecessarily
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Class Person. */
functionPerson(name) {
this.name = name;
}
Person.prototype.getName = function() {
returnthis.name;
}
/* Class Author. */
functionAuthor(name, books) {
Person.call(this, name);
this.books = books;
}
extend(Author, Person);
Author.prototype.getBooks = function() {
returnthis.books;
};
Instead of setting the prototype and constructor attributes manually, simply call the extend function immediately after the class declaration (and before you add any methods to the prototype). The only problem with this is that the name of the superclass (Person) is hardcoded within the Author declaration. It would be better to refer to it in a more general way:
This version is a little longer but provides the superclass attribute, which you can now use to make Author less tightly coupled to Person. The first four lines of the function are the same as before. The last three lines simply ensure that the constructor attribute is set correctly on the superclass (even if the superclass is the Object class itself). This will become important when you use this new superclass attribute to call the superclass’s constructor:
1
2
3
4
5
6
7
8
9
/* Class Author. */
functionAuthor(name, books) {
Author.superclass.constructor.call(this, name);
this.books = books;
}
extend(Author, Person);
Author.prototype.getBooks = function() {
returnthis.books;
};
Adding the superclass attribute also allows you to call methods directly from the superclass.This is useful if you want to override a method while still having access to the superclass’s implementation of it. For instance, to override Person’s implementation of getName with a new version,you could use Author.superclass.getName to first get the original name and then add to it:
1
2
3
4
Author.prototype.getName = function() {
var name = Author.superclass.getName.call(this);
return name + ', Author of ' + this.getBooks().join(', ');
Most methods and attributes interact with an instance of a class; static members interact with the class itself. Another way of putting it is to say that static members operate on the class-level instead of the instance-level; there is only one copy of each static member.
Here is the Book class with static attributes and methods:
numOfBooks++; // Keep track of how many Books have been instantiated
// with the private static attribute.
if(numOfBooks > 50) thrownewError('Book: Only 50 instances of Book can be '
+ 'created.');
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
}
})();
// Public static method.
Book.convertToTitleCase = function(inputString) {
...
};
// Public, non-privileged methods.
Book.prototype = {
display: function() {
...
}
};
Private and privileged members are still declared within the constructor, using var and this respectively, but the constructor is changed from a normal function to a nested function that gets returned to the variable Book. This makes it possible to create a closure where you can declare private static members. The empty parentheses after the function declaration are extremely important. They serve to execute that function immediately, as soon as the code is loaded (not when the Book constructor is called).The result of that execution is another function, which is returned and set to be the Book constructor. When Book is instantiated, this inner function is what gets called; the outer function is used only to create a closure, within which you can put private static members.
In this example, the checkIsbn method is static because there is no point in creating a new copy of it for each instance of Book. There is also a static attribute called numOfBooks, which allows you to keep track of how many times the Book constructor has been called. In this example, we use that attribute to limit the constructor to creating only 50 instances.
These private static members can be accessed from within the constructor, which means that any private or privileged function has access to them. They have a distinct advantage over these other methods in that they are only stored in memory once. Since they are declared outside of the constructor, they do not have access to any of the private attributes, and as such, are not privileged; private methods can call private static methods, but not the other way around. A rule of thumb for deciding whether a private method should be static is to see whether it needs to access any of the instance data. If it doe not need access, making the method static is more efficient (in terms of memory use) because only a copy is ever created.
Public static members are much easier to create. They are simply created directly off of the constructor, as with the previous method convertToTitleCase. This means you are essentially using the constructor as a namespace.
All public static methods could just as easily be declared as separate functions, but it is useful to bundle related behaviors together in one place. They are useful for tasks that are related to the class as a whole and not to any particular instance of it. They don’t directly depend on any of the data contained within the instances.
Constants
Constants are nothing more than variables that can’t be changed. In JavaScript, you can emulate constants by creating a private variable with an accessor but no mutator. Since constants are usually set at development time and don’t change with each instance that is created, it makes sense to create them as private static attributes. Here is how a call to get the constant UPPER_BOUND from Class would look:
1
Class.getUPPER_BOUND();
To implement this accessor, you would need a privileged static method, which we haven’t covered yet. It is created just like a privileged instance method, with the this keyword:
1
2
3
4
5
6
7
8
9
10
11
12
13
var Class = (function() {
// Constants (created as private static attributes).
var UPPER_BOUND = 100;
// Privileged static method.
this.getUPPER_BOUND() {
return UPPER_BOUND;
}
...
// Return the constructor.
returnfunction(constructorArgument) {
...
}
})();
If you have a lot of constants and don’t want to create an accessor method for each, you can create a single generic accessor method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Class = (function() {
// Private static attributes.
var constants = {
UPPER_BOUND: 100,
LOWER_BOUND: -100
}
// Privileged static method.
this.getConstant(name) {
return constants[name];
}
...
// Return the constructor.
returnfunction(constructorArgument) {
...
}
})();
Then you would get a constant by calling the single accessor:
var friend = new Person("Nicholas", 29, "Software Enginner");
friend.sayName();
这里对原型所做的修改,能够立即在所有实例中得到反映。因此,这种方法确实可以说非常完美。其中, if 语句检查的可以是初始化之后应该存在的任何属性或方法---不必用一大堆 if 语句检查每个属性和方法;只要检查其中一个即可。对于采用这种模式创建的对象,还可以使用 instanceof 操作符确定它的类型。