• 沒有找到結果。

Object Attributes

在文檔中 JavaScript: The Definitive Guide (頁 153-156)

5.7.3 “use strict”

6.8 Object Attributes

Every object has associated prototype, class, and extensible attributes. The subsections that follow explain what these attributes do and (where possible) how to query and set them.

6.8.1 The prototype Attribute

An object’s prototype attribute specifies the object from which it inherits properties.

(Review §6.1.3 and §6.2.2 for more on prototypes and property inheritance.) This is such an important attribute that we’ll usually simply say “the prototype of o” rather than “the prototype attribute of o.” Also, it is important to understand that when prototype appears in code font, it refers to an ordinary object property, not to the prototype attribute.

The prototype attribute is set when an object is created. Recall from §6.1.3 that objects created from object literals use Object.prototype as their prototype. Objects created with new use the value of the prototype property of their constructor function as their prototype. And objects created with Object.create() use the first argument to that function (which may be null) as their prototype.

In ECMAScript 5, you can query the prototype of any object by passing that object to Object.getPrototypeOf(). There is no equivalent function in ECMAScript 3, but it is often possible to determine the prototype of an object o using the expression o.constructor.prototype. Objects created with a new expression usually inherit a constructor property that refers to the constructor function used to create the object.

And, as described above, constructor functions have a prototype property that specifies the prototype for objects created using that constructor. This is explained in more detail in §9.2, which also explains why it is not a completely reliable method for determining an object’s prototype. Note that objects created by object literals or by Object.create() have a constructor property that refers to the Object() constructor.

Thus, constructor.prototype refers to the correct prototype for object literals, but does not usually do so for objects created with Object.create().

To determine whether one object is the prototype of (or is part of the prototype chain of) another object, use the isPrototypeOf() method. To find out if p is the prototype of o write p.isPrototypeOf(o). For example:

var p = {x:1}; // Define a prototype object.

var o = Object.create(p); // Create an object with that prototype.

p.isPrototypeOf(o) // => true: o inherits from p

Object.prototype.isPrototypeOf(o) // => true: p inherits from Object.prototype

Note that isPrototypeOf() performs a function similar to the instanceof operator (see

§4.9.4).

Mozilla’s implementation of JavaScript has (since the early days of Netscape) exposed the prototype attribute through the specially named __proto__ property, and you can use this property to directly query or set the prototype of any object. Using __proto__

6.8 Object Attributes | 135

Core JavaScript

is not portable: it has not been (and probably never will be) implemented by IE or Opera, although it is currently supported by Safari and Chrome. Versions of Firefox that implement ECMAScript 5 still support __proto__, but restrict its ability to change the prototype of nonextensible objects.

6.8.2 The class Attribute

An object’s class attribute is a string that provides information about the type of the object. Neither ECMAScript 3 nor ECMAScript 5 provide any way to set this attribute, and there is only an indirect technique for querying it. The default toString() method (inherited from Object.prototype) returns a string of the form:

[object class]

So to obtain the class of an object, you can invoke this toString() method on it, and extract the eighth through the second-to-last characters of the returned string. The tricky part is that many objects inherit other, more useful toString() methods, and to invoke the correct version of toString(), we must do so indirectly, using the Function.call() method (see §8.7.3). Example 6-4 defines a function that returns the class of any object you pass it.

Example 6-4. A classof() function function classof(o) {

if (o === null) return "Null";

if (o === undefined) return "Undefined";

return Object.prototype.toString.call(o).slice(8,-1);

}

This classof() function works for any JavaScript value. Numbers, strings, and booleans behave like objects when the toString() method is invoked on them, and the function includes special cases for null and undefined. (The special cases are not required in ECMAScript 5.) Objects created through built-in constructors such as Array and Date have class attributes that match the names of their constructors. Host objects typically have meaningful class attributes as well, though this is implementation-dependent.

Objects created through object literals or by Object.create have a class attribute of

“Object”. If you define your own constructor function, any objects you create with it will have a class attribute of “Object”: there is no way to specify the class attribute for your own classes of objects:

classof(null) // => "Null"

classof(1) // => "Number"

classof("") // => "String"

classof(false) // => "Boolean"

classof({}) // => "Object"

classof([]) // => "Array"

classof(/./) // => "Regexp"

classof(new Date()) // => "Date"

classof(window) // => "Window" (a client-side host object) function f() {}; // Define a custom constructor

classof(new f()); // => "Object"

6.8.3 The extensible Attribute

The extensible attribute of an object specifies whether new properties can be added to the object or not. In ECMAScript 3, all built-in and user-defined objects are implicitly extensible, and the extensibility of host objects is implementation defined. In ECMA-Script 5, all built-in and user-defined objects are extensible unless they have been converted to be nonextensible, and the extensibility of host objects is again implemen-tation defined.

ECMAScript 5 defines functions for querying and setting the extensibility of an object.

To determine whether an object is extensible, pass it to Object.isExtensible(). To make an object nonextensible, pass it to Object.preventExtensions(). Note that there is no way to make an object extensible again once you have made it nonextensible. Also note that calling preventExtensions() only affects the extensibility of the object itself.

If new properties are added to the prototype of a nonextensible object, the nonexten-sible object will inherit those new properties.

The purpose of the extensible attribute is to be able to “lock down” objects into a known state and prevent outside tampering. The extensible object attribute is often used in conjunction with the configurable and writable property attributes, and ECMAScript 5 defines functions that make it easy to set these attributes together.

Object.seal() works like Object.preventExtensions(), but in addition to making the object nonextensible, it also makes all of the own properties of that object nonconfig-urable. This means that new properties cannot be added to the object, and existing properties cannot be deleted or configured. Existing properties that are writable can still be set, however. There is no way to unseal a sealed object. You can use Object.isSealed() to determine whether an object is sealed.

Object.freeze() locks objects down even more tightly. In addition to making the object nonextensible and its properties nonconfigurable, it also makes all of the object’s own data properties read-only. (If the object has accessor properties with setter methods, these are not affected and can still be invoked by assignment to the property.) Use Object.isFrozen() to determine if an object is frozen.

It is important to understand that Object.seal() and Object.freeze() affect only the object they are passed: they have no effect on the prototype of that object. If you want to thoroughly lock down an object, you probably need to seal or freeze the objects in the prototype chain as well.

Object.preventExtensions(), Object.seal(), and Object.freeze() all return the object that they are passed, which means that you can use them in nested function invocations:

// Create a sealed object with a frozen prototype and a nonenumerable property var o = Object.seal(Object.create(Object.freeze({x:1}),

{y: {value: 2, writable: true}}));

6.8 Object Attributes | 137

Core JavaScript

在文檔中 JavaScript: The Definitive Guide (頁 153-156)