JavaScript: The Definitive Guide, Sixth Editio javaScript权威指南(第6版) pdf 文字版-文字版, javascript电子书, 和javascript 有关的电子书:

9.8.2 Defining Immutable Classes

9.8.2 Defining Immutable Classes


In addition to making properties nonenumerable, ECMAScript 5 allows us to make properties read-only, which is handy if we want to define classes whose instances are immutable. Example 9-18 is an immutable version of our Range class that does this using Object.defineProperties() and with Object.create(). It also uses Object.defineProperties() to set up the prototype object for the class, making the instance methods nonenumerable, like the methods of built-in classes. In fact, it goes further than this and makes those instance methods read-only and nondeletable, which prevents any dynamic alterations (“monkey-patching”) to the class. Finally, as an interesting trick, Example 9-18 has a constructor function that works as a factory function when invoked without the new keyword.

Example 9-18. An immutable class with read-only properties and methods

// This function works with or without 'new': a constructor and factory function

function Range(from,to) { // These are descriptors for the read-only from and to properties. var props = {

from: {value:from, enumerable:true, writable:false, configurable:false}, to: {value:to, enumerable:true, writable:false, configurable:false} };

if (this instanceof Range) // If invoked as a constructor Object.defineProperties(this, props); // Define the properties

else // Otherwise, as a factory return Object.create(Range.prototype, // Create and return a new props); // Range object with props

}

// If we add properties to the Range.prototype object in the same way, // then we can set attributes on those properties. Since we don't specify // enumerable, writable, or configurable, they all default to false. Object.defineProperties(Range.prototype, {

includes: {

value: function(x) { return this.from <= x && x <= this.to; } }, foreach: {

value: function(f) { for(var x = Math.ceil(this.from); x <= this.to; x++) f(x);

} }, toString: {

value: function() { return "(" + this.from + "..." + this.to + ")"; } } });

Example 9-18 uses Object.defineProperties() and Object.create() to define immutable and nonenumerable properties. These are powerful methods, but the property descriptor objects they require can make the code difficult to read. An alternative is to define utility functions for modifying the attributes of properties that have already been defined. Example 9-19 shows two such utility functions.

9.8 Classes in ECMAScript 5 | 239

Example 9-19. Property descriptor utilities

// Make the named (or all) properties of o nonwritable and nonconfigurable. function freezeProps(o) {

var props = (arguments.length == 1) // If 1 arg ? Object.getOwnPropertyNames(o) // use all props : Array.prototype.splice.call(arguments, 1); // else named props

props.forEach(function(n) { // Make each one read-only and permanent // Ignore nonconfigurable properties if (!Object.getOwnPropertyDescriptor(o,n).configurable) return; Object.defineProperty(o, n, { writable: false, configurable: false });

}); return o; // So we can keep using it }

// Make the named (or all) properties of o nonenumerable, if configurable. function hideProps(o) {

var props = (arguments.length == 1) // If 1 arg ? Object.getOwnPropertyNames(o) // use all props : Array.prototype.splice.call(arguments, 1); // else named props

props.forEach(function(n) { // Hide each one from the for/in loop // Ignore nonconfigurable properties if (!Object.getOwnPropertyDescriptor(o,n).configurable) return; Object.defineProperty(o, n, { enumerable: false });

}); return o; }

Object.defineProperty() and Object.defineProperties() can be used to create new properties and also to modify the attributes of existing properties. When used to define new properties, any attributes you omit default to false. When used to alter existing properties, however, the attributes you omit are left unchanged. In the hideProps() function above, for example, we specify only the enumerable attribute because that is the only one we want to modify.

With these utility functions defined, we can take advantage of ECMAScript 5 features to write an immutable class without dramatically altering the way we write classes. Example 9-20 shows an immutable Range class that uses our utility functions.

Example 9-20. A simpler immutable class

function Range(from, to) { // Constructor for an immutable Range class this.from = from; this.to = to; freezeProps(this); // Make the properties immutable

}

Range.prototype = hideProps({ // Define prototype with nonenumerable properties constructor: Range, includes: function(x) { return this.from <= x && x <= this.to; }, foreach: function(f) {for(var x=Math.ceil(this.from);x<=this.to;x++) f(x);}, toString: function() { return "(" + this.from + "..." + this.to + ")"; }

});

欢迎转载,转载请注明来自一手册:http://yishouce.com/book/1/27853.html
友情链接It题库(ittiku.com)| 版权归yishouce.com所有| 友链等可联系 admin#yishouce.com|粤ICP备16001685号-1