javascript权威指南笔记(第9章 类和模块)

Posted on 2014-09-10 21:24  liguwe  阅读(309)  评论(0编辑  收藏  举报

1、工厂函数

function range(from, to) {
    var r = inherit(range.methods);
    r.from = from;
    r.to = to;

    return r;
};


range.methods = {

    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 + ")";
    }
}
// Here are example uses of a range object.
var r = range(1, 3);                    // Create a range object
r.includes(2);                            // => true: 2 is in the range
r.foreach(console.log);                // Prints 1 2 3
console.log(r);                  // Prints (1...3)

 2、使用构造函数代替工厂函数: 注意调用时必须使用new操作符

function Range(from, to) {
    this.from = from;
    this.to = to;
}

Range.prototype = {
    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 + ")";
    } };
// Here are example uses of a range object
var r = new Range(1, 3);            // Create a range object
r.includes(2);                        // => true: 2 is in the range
r.foreach(console.log);              // Prints 1 2 3
console.log(r);                     // Prints (1...3)

 

3、constructor属性

var F = function() {};             // This is a function object.
var p = F.prototype;            // This is the prototype object associated with it.
var c = p.constructor;         // This is the function associated with the prototype.
c === F;                      // => true: F.prototype.constructor==F for any function
var o = new F();              // Create an object o of class F
o.constructor === F;          // => true: the constructor property specifies the class

 

4、比较下面两段代码的不同:都是原型方式,虽然可以保证

Range.prototype = {
    constructor: Range,       // Explicitly set the constructor back-reference
    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 + ")";
    }
};

/*预定义原型对象,这个原型对象包括constructor属性*/
Range.prototype.includes = function (x) {
    return this.from <= x && x <= this.to;
};
Range.prototype.foreach = function (f) {
    for (var x = Math.ceil(this.from); x <= this.to; x++) f(x);
};
Range.prototype.toString = function () {
    r

 

5、javascript中的java式继承:结合6来分析

  一个用于定义简单类的函数并用其定义前面的Range类:

  

function defineClass(constructor,    // A function that sets instance properties
                     methods,        // Instance methods: copied to prototype
                     statics)        // Class properties: copied to constructor
{
    if (methods) extend(constructor.prototype, methods);
    if (statics) extend(constructor, statics);
    return constructor;
}
// This is a simple variant of our Range class
var SimpleRange =
    defineClass(function (f, t) {
            this.f = f;
            this.t = t;
        }, {
            includes: function (x) {
                return this.f <= x && x <= this.t;
            },
            toString: function () {
                return this.f + "..." + this.t;
            } },
        { upto: function (t) {
            return new SimpleRange(0, t);
        } });

 

 

6、Complex.js:

注意区分:java(实例字段、实例方法、类字段、类方法)  

      javascript(构造函数对象、原型对象、实例对象)

/**********************************构造函数*/
function Complex(real, imaginary) {
    if (isNaN(real) || isNaN(imaginary)) // Ensure that both args are numbers.
        throw new TypeError();           // Throw an error if they are not.
    this.r = real;                       // The real part of the complex number.
    this.i = imaginary;                  // The imaginary part of the number.
}

/*********************实例方法*/
// Add a complex number to this one and return the sum in a new object.
Complex.prototype.add = function (that) {
    return new Complex(this.r + that.r, this.i + that.i);
};
// Multiply this complex number by another and return the product.
Complex.prototype.mul = function (that) {
    return new Complex(this.r * that.r - this.i * that.i,
            this.r * that.i + this.i * that.r);
};
// Return the real magnitude of a complex number. This is defined
// as its distance from the origin (0,0) of the complex plane.
Complex.prototype.mag = function () {
    return Math.sqrt(this.r * this.r + this.i * this.i);
};

// Return a complex number that is the negative of this one.
Complex.prototype.neg = function () {
    return new Complex(-this.r, -this.i);
};

// Convert a Complex object to a string in a useful way.
Complex.prototype.toString = function () {
    return "{" + this.r + "," + this.i + "}";
};

// Test whether this Complex object has the same value as another.
Complex.prototype.equals = function (that) {
    return that != null &&                      // must be defined and non-null
        that.constructor === Complex &&         // and an instance of Complex
        this.r === that.r && this.i === that.i; // and have the same values.
};

/*类字段*/
// Here are some class fields that hold useful predefined complex numbers.
// Their names are uppercase to indicate that they are constants.
// (In ECMAScript 5, we could actually make these properties read-only.)
Complex.ZERO = new Complex(0, 0);
Complex.ONE = new Complex(1, 0);
Complex.I = new Complex(0, 1);

/*类方法*/
// This class method parses a string in the format returned by the toString
// instance method and returns a Complex object or throws a TypeError.
Complex.parse = function (s) {
    try {          // Assume that the parsing will succeed
        var m = Complex._format.exec(s);  // Regular expression magic
        return new Complex(parseFloat(m[1]), parseFloat(m[2]));
    } catch (x) {  // And throw an exception if it fails
        throw new TypeError("Can't parse '" + s + "' as a complex number.");
    }
};
/*私有字段*
// A "private" class field used in Complex.parse() above.
// The underscore in its name indicates that it is intended for internal
// use and should not be considered part of the public API of this class. 
// 下划线表示内部类使用的函数,不属于API的部分
Complex._format = /^\{([^,]+),([^}]+)\}$/;

 

利用上面定义的complex.js

var c = new Complex(2, 3);  // Create a new object with the constructor 
var d = new Complex(c.i, c.r);   // Use instance properties of c 
c.add(d).toString();        // => "{5,5}": use instance methods
// A more complex expression that uses a class method and field 
Complex.parse(c.toString())     // Convert c to a string and back again,
    .add(c.neg())           // add its negative to it, 
    .equals(Complex.ZERO)  // and it will always equal zero

 

 

7、javascript可以模拟出java式的类,但是也有一些不能模拟出以及一些区别及解决方案(……………………)

 

8、类的扩充:

  

//给ES3中的函数类添加bind方法
if (!Function.prototype.bind) {
    Function.prototype.bind = function (o /*, args */) {
        // Code for the bind method goes here... };
    }
}

var n = 3;
n.times(function (n) {
    console.log(n + " hello");
});
//下面分别给Number,string/function等添加方法或属性
Number.prototype.times = function (f, context) {
    var n = Number(this);
    for (var i = 0; i < n; i++) f.call(context, i);
};

String.prototype.trim = String.prototype.trim || function () {
    if (!this) return this;  // Don't alter the empty string
    return this.replace(/^\s+|\s+$/g, "");  // Regular expression magic
};

Function.prototype.getName = function () {
    return this.name || this.toString().match(/function\s*([^(]*)\(/)[1];
};

 

Es3之前添加的这些方法设置为不可枚举的,Es5中可以使用Object.defineProperty设置

在很多浏览器中,可以给HTMLElement.prototype添加方法,但是ie不可以,这导致了很多实用就严重限制

 

 

9、类和类型

1、instanceof/isprototypeof

  缺点:1、无法对象获取类名、只能检测

    2、多窗口和多框架的子页面不兼容

2、contructor属性:

同样,在多个执行上下文无法工作

        function typeAndValue(x) {
            if (x == null) return "";
            switch (x.constructor) {
                case Number:return "Number:" + x;
                case String:return "String: '" + x + "'";
                case Date:return "Date: " + x;
                case RegExp:return "Regexp: " + x;
                case Complex:return "Complex: " + x;
            }
        }

 

3、构造函数的名称

function type(o) {
    var t, c, n;  // type, class, name
// Special case for the null value:
    if (o === null) return "null";
// Another special case: NaN is the only value not equal to itself:
    if (o !== o) return "nan";
// Use typeof for any value other than "object".
// This identifies any primitive value and also functions.
    if ((t = typeof o) !== "object") return t;
// Return the class of the object unless it is "Object".
// This will identify most native objects.
    if ((c = classof(o)) !== "Object") return c;
// Return the object's constructor name, if it has one
    if (o.constructor && typeof o.constructor === "function" && (n = o.constructor.getName())) return n;
// We can't determine a more specific type, so return "Object"
    return "Object";
}


// Return the class of an object.
function classof(o) {
    return Object.prototype.toString.call(o).slice(8, -1);
};
// Return the name of a function (may be "") or null for nonfunctions
Function.prototype.getName = function () {
    if ("name" in this) return this.name;
    return this.name = this.toString().match(/function\s*([^(]*)\(/)[1];
};

 

注意:并不是所有的对象都有contructor属性,也并不是所有的函数都有名字如

// This constructor has no name
var Complex = function (x, y) {
    this.r = x;
    this.i = y;
}
// This constructor does have a name
var Range = function Range(f, t) {
    this.from = f;
    this.to = t;
}

 

4、鸭式辩型

//检测一个对象(第一个参数o)是否实现了剩余参数所表示的方法

但是不能应用于内置类:

如quacks(o,Array)不能用来检测o是否实现了Array中的所有同名的方法,因为内置类的方法都是不可枚举的

/*Example 9-5. A function for duck-type checking*/
// Return true if o implements the methods specified by the remaining args.
function quacks(o /*, ... */) {
    for (var i = 1; i < arguments.length; i++) {   // for each argument after o
        var arg = arguments[i];
        switch (typeof arg) { // If arg is a:
            case 'string':  // string: check for a method with that name
                if (typeof o[arg] !== "function") return false;
                continue;
            case 'function':  // function: use the prototype object instead
// If the argument is a function, we use its prototype object
                arg = arg.prototype;
// fall through to the next case
            case 'object':  // object: check for matching methods
                for (var m in arg) { // For each property of the object
                    if (typeof arg[m] !== "function") continue; //  skip non-methods
                    if (typeof o[m] !== "function")
                        return false;
                }
        }
    }
// If we're still here, then o implements everything
    return true;
}

 

 

10 、javascript的面向对象技术:

1、集合类

function Set() {          // This is the constructor
    this.values = {};     // The properties of this object hold the set
    this.n = 0;           // How many values are in the set
    this.add.apply(this, arguments);  // All arguments are values to add
}

// Add each of the arguments to the set.
Set.prototype.add = function () {
    for (var i = 0; i < arguments.length; i++) {  // For each argument
        var val = arguments[i];                  // The value to add to the set
        var str = Set._v2s(val);                 // Transform it to a string
        if (!this.values.hasOwnProperty(str)) {  // If not already in the set
            this.values[str] = val;              // Map string to value
            this.n++;                            // Increase set size
        }
    }
    return this;                                 // Support chained method calls
};

// Remove each of the arguments from the set.
Set.prototype.remove = function () {
    for (var i = 0; i < arguments.length; i++) {  // For each argument
        var str = Set._v2s(arguments[i]);        // Map to a string
        if (this.values.hasOwnProperty(str)) {   // If it is in the set
            delete this.values[str];             // Delete it
            this.n--;                            // Decrease set size
        }
    }
    return this;                                 // For method chaining
};

// Return true if the set contains value; false otherwise.
Set.prototype.contains = function (value) {
    return this.values.hasOwnProperty(Set._v2s(value));
};

// Return the size of the set.
Set.prototype.size = function () {
    return this.n;
};

// Call function f on the specified context for each element of the set.
Set.prototype.foreach = function (f, context) {
    for (var s in this.values)                 // For each string in the set
        if (this.values.hasOwnProperty(s))    // Ignore inherited properties
            f.call(context, this.values[s]);  // Call f on the value
};

// This internal function maps any JavaScript value to a unique string.
//这是一个内部函数
Set._v2s = function (val) { switch (val) { case undefined: return 'u'; // Special primitive case null: return 'n'; // values get single-letter case true: return 't'; // codes. case false: return 'f'; default: switch (typeof val) { case 'number': return '#' + val; // Numbers get # prefix. case 'string': return '"' + val; // Strings get " prefix. default: return '@' + objectId(val); // Objs and funcs get @ } } // For any object, return a string. This function will return a different // string for different objects, and will always return the same string // if called multiple times for the same object. To do this it creates a // property on o. In ES5 the property would be nonenumerable and read-only. function objectId(o) { var prop = "|**objectid**|"; // Private property name for storing ids if (!o.hasOwnProperty(prop)) // If the object has no id o[prop] = Set._v2s.next++; // Assign it the next available return o[prop]; // Return the id } }; Set._v2s.next = 100; // Start assigning object ids at this value.

 

 

2、javascript中的枚举类型

// This function creates a new enumerated type.  The argument object specifies
// the names and values of each instance of the class. The return value
// is a constructor function that identifies the new class.  Note, however
// that the constructor throws an exception: you can't use it to create new
// instances of the type.  The returned constructor has properties that 
// map the name of a value to the value itself, and also a values array,
// a foreach() iterator function
function enumeration(namesToValues) {
    // This is the dummy constructor function that will be the return value.
   //不能使用它来创建改类型的新实例
    var enumeration = function () {
        throw "Can't Instantiate Enumerations";
    };

    // Enumerated values inherit from this object.
    var proto = enumeration.prototype = {
        constructor: enumeration,                   // Identify type
        toString: function () {
            return this.name;
        }, // Return name
        valueOf: function () {
            return this.value;
        }, // Return value
        toJSON: function () {
            return this.name;
        }    // For serialization
    };

    enumeration.values = [];  // An array of the enumerated value objects

    // Now create the instances of this new type.
    for (name in namesToValues) {         // For each value
        var e = inherit(proto);          // Create an object to represent it
        e.name = name;                   // Give it a name
        e.value = namesToValues[name];   // And a value
        enumeration[name] = e;           // Make it a property of constructor
        enumeration.values.push(e);      // And store in the values array
    }
    // A class method for iterating the instances of the class
    enumeration.foreach = function (f, c) {
        for (var i = 0; i < this.values.length; i++) f.call(c, this.values[i]);
    };

    // Return the constructor that identifies the new type
    return enumeration;
}

 

 

3、Card.js

// Define a class to represent a playing card
function Card(suit, rank) {
    this.suit = suit;         // Each card has a suit
    this.rank = rank;         // and a rank
}

// These enumerated types define the suit and rank values
Card.Suit = enumeration({Clubs: 1, Diamonds: 2, Hearts: 3, Spades: 4});
Card.Rank = enumeration({Two: 2, Three: 3, Four: 4, Five: 5, Six: 6,
    Seven: 7, Eight: 8, Nine: 9, Ten: 10,
    Jack: 11, Queen: 12, King: 13, Ace: 14});

// Define a textual representation for a card
Card.prototype.toString = function () {
    return this.rank.toString() + " of " + this.suit.toString();
};
// Compare the value of two cards as you would in poker
Card.prototype.compareTo = function (that) {
    if (this.rank < that.rank) return -1;
    if (this.rank > that.rank) return 1;
    return 0;
};

// A function for ordering cards as you would in poker
Card.orderByRank = function (a, b) {
    return a.compareTo(b);
};

// A function for ordering cards as you would in bridge 
Card.orderBySuit = function (a, b) {
    if (a.suit < b.suit) return -1;
    if (a.suit > b.suit) return 1;
    if (a.rank < b.rank) return -1;
    if (a.rank > b.rank) return  1;
    return 0;
};


// Define a class to represent a standard deck of cards
function Deck() {
    var cards = this.cards = [];     // A deck is just an array of cards
    Card.Suit.foreach(function (s) {  // Initialize the array
        Card.Rank.foreach(function (r) {
            cards.push(new Card(s, r));
        });
    });
}

// Shuffle method: shuffles cards in place and returns the deck
Deck.prototype.shuffle = function () {
    // For each element in the array, swap with a randomly chosen lower element
    var deck = this.cards, len = deck.length;
    for (var i = len - 1; i > 0; i--) {
        var r = Math.floor(Math.random() * (i + 1)), temp;     // Random number
        temp = deck[i], deck[i] = deck[r], deck[r] = temp; // Swap
    }
    return this;
};

// Deal method: returns an array of cards
Deck.prototype.deal = function (n) {
    if (this.cards.length < n) throw "Out of cards";
    return this.cards.splice(this.cards.length - n, n);
};

// Create a new deck of cards, shuffle it, and deal a bridge hand
var deck = (new Deck()).shuffle();
var hand = deck.deal(13).sort(Card.orderBySuit);

 

 

4、标准转换方法:

//对象类型的转换方法有时候应该有意为之

// Add these methods to the Set prototype object.
extend(Set.prototype, {
// Convert a set to a string
    toString: function () {
        var s = "{",
            i = 0;
        this.foreach(function (v) {
            s += ((i++ > 0) ? ", " : "") + v;
        });
        return s + "}";
    },
// Like toString, but call toLocaleString on all values
    toLocaleString: function () {
        var s = "{",
            i = 0;
        this.foreach(function (v) {
            if (i++ > 0) s += ", ";
            if (v == null) s += v; // null & undefined
            else s += v.toLocaleString(); // all others
        });
        return s + "}";
    },
// Convert a set to an array of values
    toArray: function () {
        var a = [];
        this.foreach(function (v) {
            a.push(v);
        });
        return a;
    } });
// Treat sets like arrays for the purposes of JSON stringification.
Set.prototype.toJSON = Set.prototype.toArray;

 

 

两个对象的比较方法:

对于简单的类,可以先比较他们的constructor 然后在比较这两个对象的实例属性例如:

// The Range class overwrote its constructor property. So add it now.
Range.prototype.constructor = Range;
// A Range is not equal to any nonrange.
// Two ranges are equal if and only if their endpoints are equal.
Range.prototype.equals = function (that) {
    if (that == null) return false; // Reject null and undefined
    if (that.constructor !== Range) return false; // Reject non-ranges
// Now return true if and only if the two endpoints are equal.
    return this.from == that.from && this.to == that.to;
}

 

对于复杂的类,如Set类,应进行更深层次的比较

Set.prototype.equals = function (that) { // Shortcut for trivial case
    if (this === that) return true;
// If the that object is not a set, it is not equal to this one.
// We use instanceof to allow any subclass of Set.
// We could relax this test if we wanted true duck-typing.
// Or we could strengthen it to check this.constructor == that.constructor
// Note that instanceof properly rejects null and undefined values
    if (!(that instanceof Set)) return false;
// If two sets don't have the same size, they're not equal
    if (this.size() != that.size()) return false;
// Now check whether every element in this is also in that.
// Use an exception to break out of the foreach if the sets are not equal.
    try {
        this.foreach(function (v) {
            if (!that.contains(v)) throw false;
        });
        return true; // All elements matched: sets are equal.
    } catch (x) {
        if (x === false) return false; // An element in this is not in that.
        throw x; // Some other exception: rethrow it.
    }
};

 

 

 

 

 

 

 

 

 

 

 

方法借用:

var generic = {
    // Returns a string that includes the name of the constructor function
    // if available and the names and values of all noninherited, nonfunction
    // properties.
    toString: function () {
        var s = '[';
        // If the object has a constructor and the constructor has a name,
        // use that class name as part of the returned string.  Note that
        // the name property of functions is nonstandard and not supported
        // everywhere.
        if (this.constructor && this.constructor.name)
            s += this.constructor.name + ": ";

        // Now enumerate all noninherited, nonfunction properties
        var n = 0;
        for (var name in this) {
            if (!this.hasOwnProperty(name)) continue;   // skip inherited props
            var value = this[name];
            if (typeof value === "function") continue;  // skip methods
            if (n++) s += ", ";
            s += name + '=' + value;
        }
        return s + ']';
    },

    // Tests for equality by comparing the constructors and instance properties
    // of this and that.  Only works for classes whose instance properties are
    // primitive values that can be compared with ===.
    // As a special case, ignore the special property added by the Set class.
    equals: function (that) {
        if (that == null) return false;
        if (this.constructor !== that.constructor) return false;
        for (var name in this) {
            if (name === "|**objectid**|") continue;     // skip special prop.
            if (!this.hasOwnProperty(name)) continue;    // skip inherited 
            if (this[name] !== that[name]) return false; // compare values
        }
        return true;  // If all properties matched, objects are equal.
    }
};

 

 

私有状态:

function Range(from, to) {
    // Don't store the endpoints as properties of this object. Instead
    // define accessor functions that return the endpoint values.
    // These values are stored in the closure.
    this.from = function () {
        return from;
    };
    this.to = function () {
        return to;
    };
}

// The methods on the prototype can't see the endpoints directly: they have
// to invoke the accessor methods just like everyone else.
Range.prototype = {
    constructor: Range,
    includes: function (x) {
        return this.from() <= x && x <= this.to();
    },
    foreach: function (f) {
        for (var x = Math.ceil(this.from()), max = this.to(); x <= max; x++) f(x);
    },
    toString: function () {
        return "(" + this.from() + "..." + this.to() + ")";
    }
};

 

 

 

 

定义子类:

// A simple function for creating simple subclasses
function defineSubclass(superclass,  // Constructor of the superclass
                        constructor, // The constructor for the new subclass
                        methods,     // Instance methods: copied to prototype
                        statics)     // Class properties: copied to constructor
{
    // Set up the prototype object of the subclass
    constructor.prototype = inherit(superclass.prototype);
    constructor.prototype.constructor = constructor;
    // Copy the methods and statics as we would for a regular class
    if (methods) extend(constructor.prototype, methods);
    if (statics) extend(constructor, statics);
    // Return the class
    return constructor;
}

// We can also do this as a method of the superclass constructor
Function.prototype.extend = function (constructor, methods, statics) {
    return defineSubclass(this, constructor, methods, statics);
};

 

一个简单的子类:

// The constructor function 
function SingletonSet(member) {
    this.member = member;   // Remember the single member of the set
}

// Create a prototype object that inherits from the prototype of Set.
SingletonSet.prototype = inherit(Set.prototype);

// Now add properties to the prototype.
// These properties override the properties of the same name from Set.prototype.
extend(SingletonSet.prototype, {
    // Set the constructor property appropriately
    constructor: SingletonSet,
    // This set is read-only: add() and remove() throw errors
    add: function () {
        throw "read-only set";
    },
    remove: function () {
        throw "read-only set";
    },
    // A SingletonSet always has size 1
    size: function () {
        return 1;
    },
    // Just invoke the function once, passing the single member.
    foreach: function (f, context) {
        f.call(context, this.member);
    },
    // The contains() method is simple: true only for one value
    contains: function (x) {
        return x === this.member;
    }
});