/* --- MooTools: the javascript framework web build: - http://mootools.net/core/58f64dfefa42b08cb566c95a6244e613 packager build: - packager build Core/Class ... */ /* --- name: Core description: The heart of MooTools. license: MIT-style license. copyright: Copyright (c) 2006-2012 [Valerio Proietti](http://mad4milk.net/). authors: The MooTools production team (http://mootools.net/developers/) inspiration: - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php) - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) provides: [Core, MooTools, Type, typeOf, instanceOf, Native] ... */ (function(){ this.MooTools = { version: '1.4.5', build: 'ab8ea8824dc3b24b6666867a2c4ed58ebb762cf0' }; // typeOf, instanceOf var typeOf = this.typeOf = function(item){ if (item == null) return 'null'; if (item.$family != null) return item.$family(); if (item.nodeName){ if (item.nodeType == 1) return 'element'; if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace'; } else if (typeof item.length == 'number'){ if (item.callee) return 'arguments'; if ('item' in item) return 'collection'; } return typeof item; }; var instanceOf = this.instanceOf = function(item, object){ if (item == null) return false; // 在这可以获得所有对象的$constructor, 也包括字符串, 数字,如'aaaa'.$constructor === String(); var constructor = item.$constructor || item.constructor; while (constructor){ if (constructor === object) return true; /* 主要用来判定Class类的, Class的Matutors.extends方法会指定勾造函数的parent; var A=new Class({ initialize:function(){ } }) var B=new Class({ Extends:A, initialize:function(){ } }) new Type('a',A); new Type('b',B); var b=new B(); console.log( instanceOf(b,A)); */ constructor = constructor.parent; } /* http://ajaxian.com/archives/working-aroung-the-instanceof-memory-leak 有三个对象会导致这个问题 window document element 举例,if (window instanceof Object) 每次刷新会增加50kb的内存,原因是这三个对象在IE下不是真正的对象,没有hasOwnProperty 及valueOf方法。 */ /*<ltIE8>*/ if (!item.hasOwnProperty) return false; /*</ltIE8>*/ /* 借用 instanceof 进行深度检测,那么 instanceOf(Array, Function) true, instance of (Array, Object) true. */ return item instanceof object; }; // Function overloading var Function = this.Function; var enumerables = true; for (var i in {toString: 1}) enumerables = null; if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor']; Function.prototype.overloadSetter = function(usePlural){ var self = this; return function(a, b){ if (a == null) return this; if (usePlural || typeof a != 'string'){ for (var k in a) self.call(this, k, a[k]); if (enumerables) for (var i = enumerables.length; i--;){ k = enumerables[i]; if (a.hasOwnProperty(k)) self.call(this, k, a[k]); } } else { self.call(this, a, b); } return this; }; }; Function.prototype.overloadGetter = function(usePlural){ var self = this; return function(a){ var args, result; if (typeof a != 'string') args = a; else if (arguments.length > 1) args = arguments; else if (usePlural) args = [a]; if (args){ result = {}; for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]); } else { result = self.call(this, a); } return result; }; }; Function.prototype.extend = function(key, value){ this[key] = value; }.overloadSetter(); Function.prototype.implement = function(key, value){ this.prototype[key] = value; }.overloadSetter(); // From var slice = Array.prototype.slice; Function.from = function(item){ return (typeOf(item) == 'function') ? item : function(){ return item; }; }; Array.from = function(item){ if (item == null) return []; return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item]; }; Number.from = function(item){ var number = parseFloat(item); return isFinite(number) ? number : null; }; String.from = function(item){ return item + ''; }; // hide, protect Function.implement({ hide: function(){ this.$hidden = true; return this; }, protect: function(){ this.$protected = true; return this; } }); // Type var Type = this.Type = function(name, object){ if (name){ var lower = name.toLowerCase(); var typeCheck = function(item){ return (typeOf(item) == lower); }; Type['is' + name] = typeCheck; if (object != null){ /* 在开头的typeOf函数会调用这个$family, var A=function(){}; new Type('Atype',A); var a=new A(); console.log(typeOf(a));将会输出 atype; A.prototype.$family=function(){return "atype";}, 所以实例会从原型中继承 $family方法。 注:这里可以不需要圆括号 object.prototype.$family=function(){return lower;}.hide(); */ object.prototype.$family = (function(){ return lower; }).hide(); } } if (object == null) return null; /* var A=function(){} var aType=new Type('A',A); this 指向 aType实例,这样在object 里就可以调用 type内的实例方法,如 A.mirror 方法。即将 type内的方法通过extend 变成 A的静态方法,而不是实例方法。 */ object.extend(this); /* 用于instanceOf(Array,Type)的检测 第一句的作用是判断 instanceOf(Array,Type) 为true 第二句的作用是判断 var a=[1,2,3] instanceOf(a, Array) true; */ object.$constructor = Type; object.prototype.$constructor = object; return object; }; var toString = Object.prototype.toString; Type.isEnumerable = function(item){ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' ); }; /* 注解ID: 勾子函数 勾子函数,主要运用去implement方法内, 用来存取在implement方法执行必须执行的方法, 通过mirror将要先执行的函数添加进去。 添加的方法有两个 1. 如果给mirror传递的为一个普能函数,则在执行的implement, 先调用这个普能的函数 2. 如果给mirror传凝视的为一个Type类别 B, 则传递的类别也将拥有这个implement的方法。 如, var A=function(){}; var B=function(){}; A.prototype=new String(); B.prototype=new String(); var typeA=new Type('a',A); var typeB=new Type('b',B); typeA.mirror(function(){ console.log(' general functin') }); typeA.mirror(typeB); typeA.implement('sayHello',function(arg1){ console.log(arg1);}); // 此时会输出 'general funcion' var a= new A(); var b=new B(); a.sayHello('this is a'); // 输出this is a. b.sayHello('this is b'); // 输出this is b. B中没有定义,但通过A的 勾子函数也实现了sayHello这个功能。 */ var hooks = {}; var hooksOf = function(object){ var type = typeOf(object.prototype); return hooks[type] || (hooks[type] = []); }; var implement = function(name, method){ if (method && method.$hidden) return; var hooks = hooksOf(this); for (var i = 0; i < hooks.length; i++){ /*参见注解 勾子函数 202行*/ var hook = hooks[i]; if (typeOf(hook) == 'type'){ implement.call(hook, name, method); } else hook.call(this, name, method); } /* 如果 A 的原型方法中没有 name这个方法,或者 有 name这个方法,name方法没有写保护,则添加这个 name方法到 A的原型方法中。 举例请见下一步 */ var previous = this.prototype[name]; if (previous == null || !previous.$protected) this.prototype[name] = method; /* 上一步是将name方法扩展到原型方法中, 此步将方法扩展到 A类的静态方法中,传入的第一个参数为 实例对像, 静态方法不会被重写 举例: var A=function(){} new Type('A',A); var a=new A(); A.implement('test',function(){ console.log('test');}) A.implement('test',function(){ console.log('test1'):}); A.test(a) // 输出test a.test(); //输出test1, 因为实例方法可以被重写 */ if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){ return method.apply(item, slice.call(arguments, 1)); }); }; var extend = function(name, method){ if (method && method.$hidden) return; var previous = this[name]; if (previous == null || !previous.$protected) this[name] = method; }; Type.implement({ implement: implement.overloadSetter(), extend: extend.overloadSetter(), /* 方法别名的应用 var str = 'howdy'; // implement the original String.implement({ repeat: function(times){ var arr = []; while (times--) arr.push(this); return arr.join(''); } }); console.log(str.repeat(2)); // 'howdyhowdy' // alias repeat String.alias('again', 'repeat'); console.log(str.again == str.repeat); // true console.log(str.again(2)); // 'howdyhowdy' */ alias: function(name, existing){ implement.call(this, name, this.prototype[existing]); }.overloadSetter(), /*参见注解 勾子函数 202行*/ mirror: function(hook){ hooksOf(this).push(hook); return this; } }); /* new Type('Type', Type) 主要是将 Type构造函数的 object.prototype.$family='type', 那么 object.extend(this) 就能获得这个值。 相当于设置 Type.prototype.$family=function(){return 'type'}; * 如果不调用 new Type('Type', Type); * * 那么 var a= new Type('a',A) 返回的 数据将为 function, 而不是 Type. * * */ new Type('Type', Type); // Default Types var force = function(name, object, methods){ var isType = (object != Object), prototype = object.prototype; if (isType) object = new Type(name, object); /* 将指写的方法保护起来,不能通过implement函数重写 例: var A=function(){} A.prototype=new Array(); new Type('a',A).implement('push',function(){}) // 不会重写Array 里面的push方法 */ for (var i = 0, l = methods.length; i < l; i++){ var key = methods[i], generic = object[key], proto = prototype[key]; if (generic) generic.protect(); /*为什么不简单的设置为proto.protect() ? */ if (isType && proto) object.implement(key, proto.protect()); } if (isType){ /* 遍列内置对象的方法,不能枚举的需要在methods数组中指定。如果methods的数组中有可枚举的方法,那么会 被调用两次fn, 看起来有bug. */ var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]); object.forEachMethod = function(fn){ if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ fn.call(prototype, prototype[methods[i]], methods[i]); } for (var key in prototype) fn.call(prototype, prototype[key], key) }; } return force; }; force('String', String, [ 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase' ])('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ])('Number', Number, [ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision' ])('Function', Function, [ 'apply', 'call', 'bind' ])('RegExp', RegExp, [ 'exec', 'test' ])('Object', Object, [ /* 将 Object 的静态方法保护起来 */ 'create', 'defineProperty', 'defineProperties', 'keys', 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen' ])('Date', Date, ['now']); Object.extend = extend.overloadSetter(); Date.extend('now', function(){ return +(new Date); }); new Type('Boolean', Boolean); // fixes NaN returning as Number Number.prototype.$family = function(){ return isFinite(this) ? 'number' : 'null'; }.hide(); // Number.random Number.extend('random', function(min, max){ return Math.floor(Math.random() * (max - min + 1) + min); }); // forEach, each var hasOwnProperty = Object.prototype.hasOwnProperty; Object.extend('forEach', function(object, fn, bind){ for (var key in object){ if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object); } }); Object.each = Object.forEach; Array.implement({ forEach: function(fn, bind){ for (var i = 0, l = this.length; i < l; i++){ if (i in this) fn.call(bind, this[i], i, this); } }, each: function(fn, bind){ Array.forEach(this, fn, bind); return this; } }); // Array & Object cloning, Object merging and appending var cloneOf = function(item){ switch (typeOf(item)){ case 'array': return item.clone(); case 'object': return Object.clone(item); default: return item; } }; Array.implement('clone', function(){ var i = this.length, clone = new Array(i); while (i--) clone[i] = cloneOf(this[i]); return clone; }); var mergeOne = function(source, key, current){ switch (typeOf(current)){ case 'object': if (typeOf(source[key]) == 'object') Object.merge(source[key], current); else source[key] = Object.clone(current); break; case 'array': source[key] = current.clone(); break; default: source[key] = current; } return source; }; /* 没有破坏 Object的原始链 */ Object.extend({ merge: function(source, k, v){ if (typeOf(k) == 'string') return mergeOne(source, k, v); for (var i = 1, l = arguments.length; i < l; i++){ var object = arguments[i]; for (var key in object) mergeOne(source, key, object[key]); } return source; }, clone: function(object){ var clone = {}; for (var key in object) clone[key] = cloneOf(object[key]); return clone; }, append: function(original){ for (var i = 1, l = arguments.length; i < l; i++){ var extended = arguments[i] || {}; for (var key in extended) original[key] = extended[key]; } return original; } }); // Object-less types /* 仅仅是获得Type.isObject(item); Type.isTextNode(item) 等静态方法 */ ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){ new Type(name); }); // Unique ID var UID = Date.now(); String.extend('uniqueID', function(){ return (UID++).toString(36); }); })(); /* --- name: Array description: Contains Array Prototypes like each, contains, and erase. license: MIT-style license. requires: Type provides: Array ... */ Array.implement({ /*<!ES5>*/ every: function(fn, bind){ for (var i = 0, l = this.length >>> 0; i < l; i++){ if ((i in this) && !fn.call(bind, this[i], i, this)) return false; } return true; }, filter: function(fn, bind){ var results = []; for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){ value = this[i]; if (fn.call(bind, value, i, this)) results.push(value); } return results; }, indexOf: function(item, from){ var length = this.length >>> 0; for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){ if (this[i] === item) return i; } return -1; }, map: function(fn, bind){ var length = this.length >>> 0, results = Array(length); for (var i = 0; i < length; i++){ if (i in this) results[i] = fn.call(bind, this[i], i, this); } return results; }, some: function(fn, bind){ for (var i = 0, l = this.length >>> 0; i < l; i++){ if ((i in this) && fn.call(bind, this[i], i, this)) return true; } return false; }, /*</!ES5>*/ clean: function(){ return this.filter(function(item){ return item != null; }); }, invoke: function(methodName){ var args = Array.slice(arguments, 1); return this.map(function(item){ return item[methodName].apply(item, args); }); }, associate: function(keys){ var obj = {}, length = Math.min(this.length, keys.length); for (var i = 0; i < length; i++) obj[keys[i]] = this[i]; return obj; }, link: function(object){ var result = {}; for (var i = 0, l = this.length; i < l; i++){ for (var key in object){ if (object[key](this[i])){ result[key] = this[i]; delete object[key]; break; } } } return result; }, contains: function(item, from){ return this.indexOf(item, from) != -1; }, append: function(array){ this.push.apply(this, array); return this; }, getLast: function(){ return (this.length) ? this[this.length - 1] : null; }, getRandom: function(){ return (this.length) ? this[Number.random(0, this.length - 1)] : null; }, include: function(item){ if (!this.contains(item)) this.push(item); return this; }, combine: function(array){ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]); return this; }, erase: function(item){ for (var i = this.length; i--;){ if (this[i] === item) this.splice(i, 1); } return this; }, empty: function(){ this.length = 0; return this; }, flatten: function(){ var array = []; for (var i = 0, l = this.length; i < l; i++){ var type = typeOf(this[i]); if (type == 'null') continue; array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]); } return array; }, pick: function(){ for (var i = 0, l = this.length; i < l; i++){ if (this[i] != null) return this[i]; } return null; }, hexToRgb: function(array){ if (this.length != 3) return null; var rgb = this.map(function(value){ if (value.length == 1) value += value; return value.toInt(16); }); return (array) ? rgb : 'rgb(' + rgb + ')'; }, rgbToHex: function(array){ if (this.length < 3) return null; if (this.length == 4 && this[3] == 0 && !array) return 'transparent'; var hex = []; for (var i = 0; i < 3; i++){ var bit = (this[i] - 0).toString(16); hex.push((bit.length == 1) ? '0' + bit : bit); } return (array) ? hex : '#' + hex.join(''); } }); /* --- name: String description: Contains String Prototypes like camelCase, capitalize, test, and toInt. license: MIT-style license. requires: Type provides: String ... */ String.implement({ test: function(regex, params){ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this); }, contains: function(string, separator){ return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1; }, trim: function(){ return String(this).replace(/^\s+|\s+$/g, ''); }, clean: function(){ return String(this).replace(/\s+/g, ' ').trim(); }, camelCase: function(){ return String(this).replace(/-\D/g, function(match){ return match.charAt(1).toUpperCase(); }); }, hyphenate: function(){ return String(this).replace(/[A-Z]/g, function(match){ return ('-' + match.charAt(0).toLowerCase()); }); }, capitalize: function(){ return String(this).replace(/\b[a-z]/g, function(match){ return match.toUpperCase(); }); }, escapeRegExp: function(){ return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); }, toInt: function(base){ return parseInt(this, base || 10); }, toFloat: function(){ return parseFloat(this); }, hexToRgb: function(array){ var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); return (hex) ? hex.slice(1).hexToRgb(array) : null; }, rgbToHex: function(array){ var rgb = String(this).match(/\d{1,3}/g); return (rgb) ? rgb.rgbToHex(array) : null; }, substitute: function(object, regexp){ return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ if (match.charAt(0) == '\\') return match.slice(1); return (object[name] != null) ? object[name] : ''; }); } }); /* --- name: Function description: Contains Function Prototypes like create, bind, pass, and delay. license: MIT-style license. requires: Type provides: Function ... */ Function.extend({ attempt: function(){ for (var i = 0, l = arguments.length; i < l; i++){ try { return arguments[i](); } catch (e){} } return null; } }); Function.implement({ attempt: function(args, bind){ try { return this.apply(bind, Array.from(args)); } catch (e){} return null; }, /*<!ES5-bind>*/ bind: function(that){ var self = this, args = arguments.length > 1 ? Array.slice(arguments, 1) : null, F = function(){}; var bound = function(){ var context = that, length = arguments.length; if (this instanceof bound){ F.prototype = self.prototype; context = new F; } var result = (!args && !length) ? self.call(context) : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments); return context == that ? result : context; }; return bound; }, /*</!ES5-bind>*/ pass: function(args, bind){ var self = this; if (args != null) args = Array.from(args); return function(){ return self.apply(bind, args || arguments); }; }, delay: function(delay, bind, args){ return setTimeout(this.pass((args == null ? [] : args), bind), delay); }, periodical: function(periodical, bind, args){ return setInterval(this.pass((args == null ? [] : args), bind), periodical); } }); /* --- name: Number description: Contains Number Prototypes like limit, round, times, and ceil. license: MIT-style license. requires: Type provides: Number ... */ Number.implement({ limit: function(min, max){ return Math.min(max, Math.max(min, this)); }, round: function(precision){ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0); return Math.round(this * precision) / precision; }, times: function(fn, bind){ for (var i = 0; i < this; i++) fn.call(bind, i, this); }, toFloat: function(){ return parseFloat(this); }, toInt: function(base){ return parseInt(this, base || 10); } }); Number.alias('each', 'times'); (function(math){ var methods = {}; math.each(function(name){ if (!Number[name]) methods[name] = function(){ return Math[name].apply(null, [this].concat(Array.from(arguments))); }; }); Number.implement(methods); })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']); /* --- name: Class description: Contains the Class Function for easily creating, extending, and implementing reusable Classes. license: MIT-style license. requires: [Array, String, Function, Number] provides: Class ... */ (function(){ var Class = this.Class = new Type('Class', function(params){ if (instanceOf(params, Function)) params = {initialize: params}; var newClass = function(){ /* this 指向通过 new newClass(arge) 构造的实例 一步步讲解一下 var A=function(param){ reset(this); } function reset(object){ for(var key in object) console.log(key); } 1. 如果A只是简单的构造函数,那么this的prototype为Oject, 即不输出任何内容 2. 如果指写A的原型为new String, 而String的prototype有定义方法,则会输出定议的方法 3. 如果在A的构造函数里面指定了this.a=param, 则reset里面也会输出a. 4. 如果将方法 b指定为A的原型, A.prototype.b=function(){}, 则reset里面也会输出b. 所以this指向的就是newClass的实例。 下面讲解下这个 this 会有哪些值。 implement 会加param 添加到这个newClass构造函数的原型上。 那么 newClass将会引用这些个属性或方法 */ reset(this); if (newClass.$prototyping) return this; this.$caller = null; var value = (this.initialize) ? this.initialize.apply(this, arguments) : this; this.$caller = this.caller = null; return value; /* extend,将Class对象的上的一些方法,设置为newClass类的静态方法,如implement var A=new Class({a:'hello',say:function(){console.log('hello');}); 若没有设置 extend 那么,A.implement只是调用 Function原型中设置的implement, 右设置了extend,那么A.implemnt会调用之后Class定义的implemnt. 通过extend(this),可以不将Class做为newClass的原型,只是将Class的方法复制到newClass上。 var B= new Class() B instanceof Class //false; */ }.extend(this).implement(params); /* 如果设置了这个 var B = new Class() instanceOf(B,Class); //true; */ newClass.$constructor = Class; newClass.prototype.$constructor = newClass; newClass.prototype.parent = parent; return newClass; }); var parent = function(){ if (!this.$caller) throw new Error('The method "parent" cannot be called.'); var name = this.$caller.$name, parent = this.$caller.$owner.parent, previous = (parent) ? parent.prototype[name] : null; if (!previous) throw new Error('The method "' + name + '" has no parent.'); return previous.apply(this, arguments); }; /* 将传入的对象进行重置,即只做传值处理,不会改变原始的对象。 obj={text:'Hello,Robin'}; var B=new Class({a:'hello','say':function(){console.log('annnn')},obj:obj}); var b=new B(); b.obj.text='hello'; console.log(obj.text); console.log(b.obj.text); */ var reset = function(object){ for (var key in object){ var value = object[key]; switch (typeOf(value)){ case 'object': var F = function(){}; F.prototype = value; object[key] = reset(new F); break; case 'array': object[key] = value.clone(); break; } } return object; }; var wrap = function(self, key, method){ if (method.$origin) method = method.$origin; var wrapper = function(){ /* var A=new Class({ say:function(){ console.log('hi A'); } }) var B=new Class({ Extends:A, say:function(){ this.parent(); console.log('hi B') } }) var C=new Class({ Extends:B, say:function(){ this.parent(); console.log('hi C'); } }) var c=new C('test'); c.say(); 调用过程如下: 1. c.say(), 设置 caller, current, this->caller,$this.$caller= c.say为空 2. 完成以上两个值后,调用this.parent(), c.say()还没有完成,到 b.say(), caller=null, current=c.say(), this->caller =c.say(), this->$caller=b.say(); 3.完成设置后,调用 this.parent(), b.say 还没有完成,到 a.say(),caller=c.say(), current=b.say(), this->caller=b.say(), this->$caller=a.say(); 4.因为 A中没有parent(),log "hi A" , 调用链重置, 此时this->caller=c.say(), $this->$caller=b.say(); 参考3. 5. 返回到 B, log "hi B" ,调用链重置 $this->caller=null, $this->current=c.say(), 参考2. 6. 返厍到C, log 'Hi c', 设用链重置 $this->caller=null , $this-current=null 参考1。 */ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.'); // current用来保存上一个方法, caller保存上上一个方法, this.$caller 为当前方法, this.caller为上一个方法 var caller = this.caller, current = this.$caller; this.caller = current; this.$caller = wrapper; var result = method.apply(this, arguments); this.$caller = current; this.caller = caller; return result; }.extend({$owner: self, $origin: method, $name: key}); return wrapper; }; var implement = function(key, value, retain){ /* 如果有指定特定的函数,如果有在Class.Mutators, 则按照Class.Mutators里面指定的方法执行。 同时执行完成后,需要Class.Mutators没有指定返回值, if(varlue==null) 为true, 直接退出implement。 */ if (Class.Mutators.hasOwnProperty(key)){ value = Class.Mutators[key].call(this, value); if (value == null) return this; } if (typeOf(value) == 'function'){ if (value.$hidden) return this; this.prototype[key] = (retain) ? value : wrap(this, key, value); } else { Object.merge(this.prototype, key, value); } // 因为调用implement都是调用overloadSetter, 而它是不需要有返回值的。 return this; }; var getInstance = function(klass){ /* 通过$prototyping, 不让父类创建一个实例,只是调用父类的reset方法,即获得父类的原型属性 var A=new Class({ name:'robin', email:'154371199', implement:function(){ console.log('hello'); } }) var B=new Class({ Extedns:A }) var b=new B(); console.log(b.name); */ klass.$prototyping = true; var proto = new klass; delete klass.$prototyping; return proto; }; Class.implement('implement', implement.overloadSetter()); Class.Mutators = { Extends: function(parent){ this.parent = parent; this.prototype = getInstance(parent); }, Implements: function(items){ Array.from(items).each(function(item){ var instance = new item; for (var key in instance) implement.call(this, key, instance[key], true); }, this); } }; })();