正在重构整个框架,让命名空间对象dom也出于同一个继承体系下,就像mootools1.3的Type对象那样。
v2的目标大致如下:
- 减少入侵性,只保留ECMA262v5及极个别最有用的扩展,lang模块将与核心模块合而为一
- 模块即纯对象,去掉没有多大用处的protect方法,实现extend与include都能调用父方法
- 去掉构造器中的智能调用父构造器的功能,以后调用父方法统一为this._super()
- 增加不使用new关键字就能进行实例化(需要在配置对象中配置)
//by 司徒正美 http://www.cnblogs.com/rubylouvre/ 2010.10.14
var
PROTO = "prototype",
CTOR = "constructor",
A_proto = Array[PROTO],
A_slice = A_proto.slice,
to_s = Object[PROTO].toString,
is = function (obj,type) {
return (type === "Object" && obj === Object(obj)) ||
(type === "Array" && Array.isArray && Array.isArray(obj)) || // ECMA-5 15.4.3.2
(type === "Null" && obj === null) ||
(type === "Undefined" && obj === void 0 ) ||
obj && to_s.call(obj).slice(8,-1) === type;
}
function extend(target,source){
for(var name in source)
if(source.hasOwnProperty(name) && !target[name]){
target[name] = source[name];
}
return target;
}
//Object扩展
//fix ie for..in bug
var _dontEnum = [ 'propertyIsEnumerable', 'isPrototypeOf','hasOwnProperty','toLocaleString', 'toString', 'valueOf', 'constructor'];
for (var i in {
toString: 1
}) _dontEnum = false;
//第二个参数仅在浏览器支持Object.defineProperties时可用
extend(Object,{
create:function( proto, props ) {//ecma262v5 15.2.3.5
var ctor = function( ps ) {
if ( ps && Object.defineProperties )
Object.defineProperties( this, ps );
};
ctor[PROTO] = proto;
return new ctor( props );
},
keys: function(obj){//ecma262v5 15.2.3.14
var result = [],dontEnum = _dontEnum,length = dontEnum.length;
for(var key in obj ) if(obj.hasOwnProperty(key)){
result.push(key)
}
if(dontEnum){
while(length){
key = dontEnum[--length];
if(obj.hasOwnProperty(key)){
result.push(key);
}
}
}
return result;
}
});
//用于创建javascript1.6 Array的迭代器
function iterator(vars, body, ret) {
return eval('[function(fn,scope){'+
'for(var '+vars+'i=0,l=this.length;i<l;i++){'+
body.replace('_', 'fn.call(scope,this[i],i,this)') +
'}' +
ret +
'}]')[0];
};
//注释照搬FF官网
extend(Array[PROTO],{
//定位类 返回指定项首次出现的索引。
indexOf: function (el, index) {
var n = this.length, i = ~~index;
if (i < 0) i += n;
for (; i < n; i++)
if ( this[i] === el) return i;
return -1;
},
//定位类 返回指定项最后一次出现的索引。
lastIndexOf: function (el, index) {
var n = this.length,
i = index == null ? n - 1 : index;
if (i < 0) i = Math.max(0, n + i);
for (; i >= 0; i--)
if (this[i] === el) return i;
return -1;
},
//迭代类 在数组中的每个项上运行一个函数,若所有结果都返回真值,此方法亦返回真值。
forEach : iterator('', '_', ''),
//迭代类 在数组中的每个项上运行一个函数,并将函数返回真值的项作为数组返回。
filter : iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'),
//迭代类 在数组中的每个项上运行一个函数,并将全部结果作为数组返回。
map : iterator('r=[],', 'r[i]=_', 'return r'),
//迭代类 在数组中的每个项上运行一个函数,若存在任意的结果返回真,则返回真值。
some : iterator('', 'if(_)return true', 'return false'),
//迭代类 在数组中的每个项上运行一个函数,若所有结果都返回真值,此方法亦返回真值。
every : iterator('', 'if(!_)return false', 'return true'),
//归化类 javascript1.8 对该数组的每项和前一次调用的结果运行一个函数,收集最后的结果。
reduce: function (fn, lastResult, scope) {
if (this.length == 0) return lastResult;
var i = lastResult !== undefined ? 0 : 1;
var result = lastResult !== undefined ? lastResult : this[0];
for (var n = this.length; i < n; i++)
result = fn.call(scope, result, this[i], i, this);
return result;
},
//归化类 javascript1.8 同上,但从右向左执行。
reduceRight: function (fn, lastResult, scope) {
var array = this.concat().reverse();
return array.reduce(fn, lastResult, scope);
}
});
//修正IE67下unshift不返回数组长度的问题
//http://www.cnblogs.com/rubylouvre/archive/2010/01/14/1647751.html
if([].unshift(1) !== 1){
A_proto.unshift = function(){
var args = [0,0];
for(var i=0,n=arguments.length;i<n;i++){
args[args.length] = arguments[i]
}
A_proto.splice.apply(this, args);
return this.length; //返回新数组的长度
}
}
//String扩展
var metaObject = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},rquote = /[\x00-\x1f\\]/g;
extend(String[PROTO],{
//javascript1.5 firefox已实现
quote:function () {
var str = this.replace(rquote,function(chr){
var meta = metaObject[chr];
return meta ? meta : '\\u' + ('0000'+chr.charCodeAt(0).toString(16)).slice(-4);
});
return '"'+ str +'"';
},
//ecma262v5 15.5.4.20
//http://www.cnblogs.com/rubylouvre/archive/2009/09/18/1568794.html
trim: function(){
var str = this.replace(/^(\s|\u00A0)+/, ''),
ws = /\s/,
i = str.length;
while (ws.test(str.charAt(--i)));
return str.slice(0, i + 1);
}
});
//Math扩展
//http://www.cnblogs.com/rubylouvre/archive/2010/10/09/1846941.html
var native_random = Math.random;
Math.random = function(min, max, exact) {
if (arguments.length === 0) {
return native_random();
} else if (arguments.length === 1) {
max = min;
min = 0;
}
var range = min + (native_random()*(max - min));
return exact === void(0) ? Math.round(range) : range.toFixed(exact);
}
extend(Function[PROTO],{
//ecma262v5 15.3.4.5
bind:function(scope) {
if (arguments.length < 2 && scope===void 0) return this;
var fn = this, argv = arguments;
return function() {
var args = [], i;
for(i = 1; i < argv.length; i++)
args.push(argv[i]);
for(i = 0; i < arguments.length; i++)
args.push(arguments[i]);
return fn.apply(scope, args);
};
}
});
function _numarr(s) { // 补零用的辅助函数
var r=[],k=-1,i=0,j,a=s.split(""),z=a.length;
for(;i < z;++i){
for(j=0;j < z;++j){
r[++k]=a[i]+a[j];
}
}
return r;
}
var numarr = _numarr("0123456789");
function toISOString() {
var ms = this.getUTCMilliseconds(),
pad0 = (ms < 10) ? "00" : (ms < 100) ? "0" : "";
return this.getUTCFullYear() + '-' +
numarr[this.getUTCMonth() + 1] + '-' +
numarr[this.getUTCDate()] + 'T' +
numarr[this.getUTCHours()] + ':' +
numarr[this.getUTCMinutes()] + ':' +
numarr[this.getUTCSeconds()] + '.' +
pad0 + this.getUTCMilliseconds() + 'Z';
}
extend(Date[PROTO],{
//ecma262v515.9.5.43
toISOString:toISOString,
//ecma262v5 15.9.5.44
toJSON:toISOString
});
extend(Date,{
//ecma262v5 15.9.4.4
now : function(){
return new Date().valueOf();
}
});
全新的类工厂。
var oneObject = function(array,val){
var result = {},value = val !== void 0 ? val :1;
for(var i=0,n=array.length;i < n;i++)
result[array[i]] = value;
return result;
},
extendObject = oneObject(['_super',PROTO, 'extend', 'include','inherit','ancestors','parent']),
includeObject = oneObject(['_super',CTOR]),
classMethods = {
inherit: function(parent) {
if (parent && parent[PROTO]) {
this[PROTO] = Object.create(parent[PROTO]);//高效设置原型链
this.parent = parent;
}
this.ancestors = [];
while (parent) {//收集所有父类,用于构建方法链时查找同名方法
this.ancestors.push(parent);
parent = parent.parent;
}
return this[PROTO][CTOR] = this;
},
extend: function(){//扩展类成员
var parent = this.parent,names, name,n,method;
arguments.length && A_slice.call(arguments).filter(function(module){
return is(module,"Object");
}).forEach(function(module){
names = Object.keys(module);
n = names.length;
while(n){
name = names[--n];
//避开FF为Object私自添加的原型属性toSource watch unwatch
if(extendObject[name]===1)
continue
method = module[name];
this[name] = method;
if(is(method,"Function") && parent){
method.parent = parent[name];
method._name = name;
}
}
},this)
return this;
},
include:function(){//扩展原型成员
var parent = this.parent && this.parent[PROTO],names, name,n,method;
arguments.length && A_slice.call(arguments).filter(function(module){
return is(module,"Object");
}).forEach(function(module){
names = Object.keys(module)
n = names.length;
while(n){
name = names[--n];
if(includeObject[name]===1)
continue
method = module[name];
this[PROTO][name] = method;
if(is(method,"Function") && parent){
method.parent = parent[name];
method._name = name;
}
}
},this)
return this;
}
}
function _super() {//构建方法链
var self= arguments.callee.caller,parent = self.parent;
if(parent){
return parent.apply(this,arguments.length? arguments : self.arguments);
}else{
throw 'this method "'+ self._name +'" no super method.';
}
}
function classPropsInject(klass,props){
['extend','include'].forEach(function(name){
var modules = props[name];
if(is(modules,"Object") || is(modules,"Array")){
klass[name].apply(klass,[].concat(modules));
delete props[name];
}
})
}
function oop(obj){
obj = obj || {};
var superclass = obj.inherit; //父类
delete obj.inherit;
var nonew = !!obj.nonew;//不用new关键字进行实例化
delete obj.nonew;
var klass = function() {
var that = this;
if(that.singleton && klass.instance){
return klass.instance;
}
if(nonew && !(that instanceof klass)){
that = new klass;
}
that.init && that.init.apply(that,arguments);
if(that.singleton){
klass.instance = that;
}
return that;//为nonew准备的出口
};
extend(klass,classMethods).inherit(superclass).extend(superclass);
classPropsInject(klass,obj);
klass._super = klass[PROTO]._super = _super;
return klass.include(obj);
}
//---------------------这是示例-----
var MyFirstClass = oop({
message: "hello world",
sayHello: function() {
p(this.message);
}
});
var obj = new MyFirstClass();
obj.sayHello();//hello world
//-----------------------------------
执行清空
扩展类成员以及在类方法中调用超类同方法:
var MyMath = oop({});
MyMath.extend({
PI:3.14,
getPI:function(){
return this.PI;
}
});
var SonMath = oop({inherit:MyMath});
SonMath.extend({
getPI:function(){
return this._super()+0.0015926;
}
});
p(SonMath.getPI());
扩展原型成员,extend、include的属性可以是单个对象,也可以是对象数组。
var movable = {
run:function(){
p("能跑")
},
fly:function(){
p("能飞")
}
}
var recognition ={
watch:function(){
p("看东西")
},
smell:function(){
p("能嗅东西")
}
}
var Robot = oop({
init:function(name,type){
this.name = name;
this.type = name;
},
include:[movable,recognition]
});
var chi = new Robot("小叽","Chobits") ;
p(chi.name);
chi.watch();
chi.fly();
配置单例类
var God = oop({
init:function(name){
this.name = name;
this.alertName = function(){
p(this.name)
}
},
singleton:true//注意这里,使用singleton属性
});
var god = new God("耶和华");
god.alertName(); //alerts 耶和华
var lucifer = new God("撒旦");
lucifer.alertName(); //alerts 耶和华
p(god === lucifer )//alerts true
配置不使用new关键字使可以实例化。
var dom2 = oop({
init:function(selector){
this.selector = selector
return this;
},
nonew:true,
getSelectors : function(){
return (this.selector ||"").split(/\s+/)
}
});
p(dom2('.aaa .bbb .ccc').getSelectors())
继承示例1
var Animal = oop({
init:function(name){
this.name = name;
},
getFood:function(){
return "各种各样的食物"
},
extend:{
getClassName:function(){
return "Animal";
}
}
});
var Human = oop({
inherit:Animal,
extend:{
getClassName:function(){
return this._super()+"-->人";
}
}
});
var me = new Human("john");
p(Human.getClassName());
p(me.getFood());
var Man = oop({
inherit:Human,
init:function(name,sex){
this._super();
this.sex = sex;
},
getFood: function(){
return this._super()+",尤其是肉类";
}
});
var Genghis_Khan = new Man("成吉思汗","男");
p( Genghis_Khan.sex);
p( Genghis_Khan.name);
p( Genghis_Khan.getFood());
继承示例2
var Polygon = oop({
init:function(sides){
this.sides = sides
},
getArea:function(){
return 0 //此只是个抽象类,不能用于具体计算
}
});
p("==============Triangle===============")
var Triangle = oop({
inherit:Polygon,
init:function(base,height){
this._super(3);
this.base = base;
this.height = height;
},
getArea:function(){
return 0.5*this.base*this.height;
}
});
var t = new Triangle(2,6);
p(t.sides);
p(t.getArea());
p("==============Rectangle===============")
var Rectangle = oop({
inherit:Polygon,
init:function(length,width){
this._super(4);
this.length = length;
this.width = width;
},
getArea:function(){
return this.length*this.width;
}
});
var r = new Rectangle(7,6);
p(r.sides);
p(r.getArea(Rectangle));
p(r instanceof Polygon)
p("==============Square===============")
var Square = oop({
inherit:Rectangle,
init:function(side){
this._super(side,side);
}
})
var s = new Square(6);
p(s.sides);
p(s.getArea())
p(s instanceof Polygon)
p(s instanceof Square)