`
wenbois2000
  • 浏览: 45752 次
  • 性别: Icon_minigender_1
  • 来自: 湖南
社区版块
存档分类
最新评论

JavaScript Patterns 读书笔记(二)

    博客分类:
  • Web
阅读更多

二.Object

  • Object Constructor Catch
       You have no reason to use the new Object() constructor when you can use an object literal, but you might be inheriting legacy code written by others, so you should be aware of one “feature” of this constructor (or yet another reason not to use it). The feature in question is that the Object() constructor accepts a parameter and, depending on the value passed, it may decide to delegate the object creation to another built-in constructor and return a different object than you expect. Following are a few examples of passing a number, a string, and a boolean value to new Object(); the result is that you get objects created with a different constructor:
    // Warning: antipatterns ahead
    // an empty object
    var o = new Object();
    console.log(o.constructor === Object); // true
    // a number object
    var o = new Object(1);
    console.log(o.constructor === Number); // true
    console.log(o.toFixed(2)); // "1.00"
    // a string object
    var o = new Object("I am a string");
    console.log(o.constructor === String); // true
    // normal objects don't have a substring()
    // method but string objects do
    console.log(typeof o.substring); // "function"
    // a boolean object
    var o = new Object(true);
    console.log(o.constructor === Boolean); // true
    
     
  • Constructor’s Return Values
      When invoked with new, a constructor function always returns an object; by default it’s the object referred to by this. If you don’t add any properties to this inside of your constructor, an “empty” object is returned (“empty” aside from inheriting from the constructor’s prototype).
     Constructors implicitly return this, even when you don’t have a return statement in the function. But you can return any other object of your choosing. In the next example, a new object referenced by that is created and returned.
    var Objectmaker = function () {
    	// this `name` property will be ignored
    	// because the constructor
    	// decides to return another object instead
    	this.name = "This is it";
    	// creating and returning a new object
    	var that = {};
    	that.name = "And that's that";
    	return that;
    };
    // test
    var o = new Objectmaker();
    console.log(o.name); // "And that's that"
    
        As can you see, you have the freedom to return any object in your constructors, as long as it’s an object. Attempting to return something that’s not an object (like a string or a boolean false, for example) will not cause an error but will simply be ignored, and the object referenced by this will be returned instead(!).
  • Self-Invoking Constructor
      To address the drawback of forgeting "new" when construct a new instance and have prototype properties available to the instance objects, consider the following approach. In the constructor you check whether this is an instance of your constructor, and if not, the constructor invokes itself again,this time properly with new:
    function Waffle() {
    	if (!(this instanceof Waffle)) {
    		return new Waffle();
    	}
    	this.tastes = "yummy";
    }
    
    Waffle.prototype.wantAnother = true;
    // testing invocations
    var first = new Waffle(),
    second = Waffle();
    console.log(first.tastes); // "yummy"
    console.log(second.tastes); // "yummy"
    console.log(first.wantAnother); // true
    console.log(second.wantAnother); // true
    
     
  • Array Constructor Curiousness
         One more reason to stay away from new Array() is to avoid a possible trap that this constructor has in store for you.
         When you pass a single number to the Array() constructor, it doesn’t become the value of the first array element. It sets the length of the array instead. This means that new Array(3) creates an array with length of 3, but no actual elements. If you try to access any of the elements, you get the value undefined because the elements don’t exist. The following code example shows the different behavior when you use the literal and the constructor with a single value.
    // an array of one element
    var a = [3];
    console.log(a.length); // 1
    console.log(a[0]); // 3
    // an array of three elements
    var a = new Array(3);
    console.log(a.length); // 3
    console.log(typeof a[0]); // "undefined"
    
     
       Although this behavior might be a little unexpected, it gets worse when you pass a floating point number to new Array() as opposed to an integer. This results in an error because the floating point is not a valid value for the array’s length:
    // using array literal
    var a = [3.14];
    console.log(a[0]); // 3.14
    var a = new Array(3.14); // RangeError: invalid array length
    console.log(typeof a); // "undefined"
    
      To avoid potential errors when creating dynamic arrays at runtime, it’s much safer to stick with the array literal notation.
  • Check for Array-ness
       Using the typeof operator with array operands returns “object.”
    console.log(typeof [1, 2]); // "object"
       Although this behavior makes sense (arrays are objects), it’s not too helpful. Often you need to know if a value actually is an array. Sometimes you can see code checking for the presence of length property or some array method such as slice() to determine “array-ness.” But these checks are not robust because there’s no reason why a nonarray object shouldn’t have properties and methods with the same names. Also people sometimes use instanceof Array, but this check works incorrectly when used across frames in some IE versions. ECMAScript 5 defines a new method Array.isArray(), which returns true if the argument is an array. For example:
    Array.isArray([]); // true
    // trying to fool the check
    // with an array-like object
    Array.isArray({
    	length: 1,
    	"0": 1,
    	slice: function () {}
    }); // false
    
       If this new method is not available in your environment, you can make the check by calling the Object.prototype.toString() method. If you invoke the call() method of toString in the context of an array, it should return the string “[object Array]”. If the context is an object, it should return the string “[object Object]”. So you can do something like this:

    	if (typeof Array.isArray === "undefined") {
    		Array.isArray = function (arg) {
    			return Object.prototype.toString.call(arg) === "[object Array]";
    		};
    	}
     
  • Error Objects
       JavaScript has a number of built-in error constructors, such as Error(), SyntaxError(),TypeError(), and others, which are used with the throw statement. The error objects created by these constructors have the following properties:

              • name
                 The name property of the constructor function that created the object; it could be  the general “Error” or a more specialized constructor such as “RangeError”

              • message
                  The string passed to the constructor when creating the object
     
       The error objects have other properties, such as the line number and filename where the error happened, but these extra properties are browser extensions inconsistently implemented across browsers and are therefore unreliable. 
       On the other hand, throw works with any object, not necessarily an object created with one of the error constructors, so you can opt in for throwing your own objects. Such error objects can have the properties “name,” “message,” and any other type of information you want to pass to be handled by the catch statement. You can be creative when it comes to your custom error objects and use them to restore the application state back to normal.

    try {
    	// something bad happened, throw an error
    	throw {
    		name: "MyErrorType", // custom error type
    		message: "oops",
    		extra: "This was rather embarrassing",
    		remedy: genericErrorHandler // who should handle it
    	};
    } catch (e) {
    	// inform the user
    	alert(e.message); // "oops"
    	// gracefully handle the error
    	e.remedy(); // calls genericErrorHandler()
    }
    
         The error constructors invoked as functions (without new) behave the same as constructors (with new) and return the same error objects.




分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics