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; } });