官术网_书友最值得收藏!

Build me a prototype

As I've previously mentioned, there is currently no support for creating true classes in JavaScript. Objects created using the structure in the previous section have a fairly major drawback: creating multiple objects is not only time-consuming but also memory intensive. Each object is completely distinct from other objects created in the same fashion. This means that the memory used to hold the function definitions is not shared between all instances. What is even more fun is that you can redefine individual instances of a class without changing all of the instances. This is demonstrated in the following code:

var Castle = function(name){
  this.name = name;
  this.build = function() {
    console.log(this.name);
  };
}
var instance1 = new Castle("Winterfell");
var instance2 = new Castle("Harrenhall");
instance1.build = function(){ console.log("Moat Cailin");}
instance1.build(); //prints "Moat Cailin"
instance2.build(); //prints "Harrenhall" to the console

Altering functionality of a single instance, or really of any already defined object in this fashion, is known as monkey patching. There is some division over whether or not this is good practice. It can certainly be useful when dealing with library code but it adds great confusion. It is generally considered to be better practice to extend the existing class.

Without a proper class system, JavaScript, of course, has no concept of inheritance. What it does have is a prototype. At the most basic level, an object in JavaScript is an associative array of keys and values. Each property or function on an object is simply defined as part of this array.

You can even see this in action by accessing members of an object using the array syntax, as is shown in the following code:

var thing = { a: 7};
console.log(thing["a"]);

Tip

Accessing members of an object using the array syntax can be a very handy way to avoid using the eval function. For instance, if I had the name of the function I wanted to call in a string called funcName and I wanted to call it on an object, obj1, then I could do so by doing obj1[funcName]() instead of using a potentially dangerous call to eval. The eval function allows for arbitrary code to be executed. Allowing this on a page means that an attacker may be able to enter malicious scripts on other people's browsers.

When an object is created, its definition is inherited from a prototype. Weirdly, each prototype is also an object, so even prototypes have prototypes. Well, except for object, which is the top-level prototype. The advantage to attaching functions to the prototype is that only a single copy of the function is created; saving on memory. There are some complexities to prototypes, but you can certainly survive without knowing about them. To make use of a prototype, you need to assign functions to it, as shown in the following code:

var Castle = function(name){
  this.name = name;
}
Castle.prototype.build = function(){ console.log(this.name);}
var instance1 = new Castle("Winterfell");
instance1.build();

One thing to note is that only the functions are assigned to the prototype. Instance variables such as name are still assigned to the instance. As these are unique to each instance, there is no real impact on the memory usage.

In many ways, a prototypical language is more powerful than a class-based inheritance model.

If you make a change to the prototype of an object at a later date, then all the objects which share that prototype, are updated with the new function. This removes some of the concerns expressed about monkey typing. An example of this behavior is shown in the following code:

var Castle = function(name){
  this.name = name;
};
Castle.prototype.build = function(){
  console.log(this.name);
};
var instance1 = new Castle("Winterfell");
Castle.prototype.build = function(){
  console.log(this.name.replace("Winterfell", "Moat Cailin"));
}
instance1.build();//prints "Moat Cailin" to the console

When building up objects, you should be sure to take advantage of the prototype object whenever possible.

Now we know about prototypes. There is an alternative approach to building objects in JavaScript, and that is to use the Object.create function. This is a new syntax introduced in ECMAScript 5, which is:

Object.create(prototype [, propertiesObject ] )

The create syntax will build a new object based on the given prototype. You can also pass in a propertiesObject parameter that describes additional fields on the created object. These descriptors consist of a number of optional fields:

  • writable: This dictates whether the field should be writable
  • configurable: This dictates whether the files should be removable from the object, or support further configuration after creation
  • enumerable: This checks whether the property can be listed during an enumeration of the object's properties
  • value: This is the default value of the field

It is also possible to assign a get and set function within the descriptor that acts as getters and setters for some other internal property.

Using object.create for our Castle class, we can build an instance using Object.create, as shown in the following code:

var instance3 = Object.create(Castle.prototype, {name: { value: "Winterfell", writable: false}});
instance3.build();
instance3.name="Highgarden";
instance3.build();

You'll notice that we explicitly define the name field. The Object.create function bypasses the constructor, so the initial assignment we described in the preceding code won't be called. You might also notice that writeable is set to false. The result of this is that the reassignment of name to "Highgarden" has no effect. The output is:

Winterfell
Winterfell
主站蜘蛛池模板: 张家界市| 固始县| 阳新县| 宁城县| 吉木萨尔县| 广饶县| 嘉兴市| 沁源县| 文化| 阿图什市| 蒙山县| 铜梁县| 汝南县| 丹东市| 吴旗县| 北海市| 汾西县| 玛多县| 夹江县| 曲靖市| 汾阳市| 新乡市| 彭州市| 兴海县| 莱州市| 云阳县| 安宁市| 万全县| 万年县| 沙雅县| 互助| 垦利县| 中牟县| 什邡市| 清苑县| 安图县| 瓦房店市| 丹阳市| 辽阳市| 澳门| 邯郸县|