zoukankan      html  css  js  c++  java
  • javascript权威指南笔记(第9章 类和模块)

    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(/functions*([^(]*)(/)[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(/functions*([^(]*)(/)[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;
        }
    });
  • 相关阅读:
    RabbitMQ(四)
    RabbitMQ(三)
    RabbitMQ(二)
    定位
    响应式布局
    学习前端的一些心得
    css样式大全
    常用标签
    HTML
    app 被拒绝原因
  • 原文地址:https://www.cnblogs.com/liguwe/p/3965185.html
Copyright © 2011-2022 走看看