// 方法的链式调用
/*
链式调用其实只不过是一种语法招数。它能让你通过重用一个初始操作来达到少量代码表达复杂操作的目的。这种技术包含两个部分:一个创建代表HTML元素的对象的工厂,以及一批对这个HTML元素执行某些操作的方法。每一个这种方法都可以在方法名前附上一个圆点后加入调用链中。方法的链式调用可以被视为选择网页上的一个元素对其进行一个或多个操作的过程。
*/
// without chaining
addEvent($('example'), 'click', function () {
setStyle(this, 'color', 'green');
show(this);
});
// with chaining
$('example').addEvent('click', function () {
$(this).setStyle('color', 'green').show();
});
// 调用链的结构
/*
把DOM集合的函数改造为一个构造器,把那些元素作为数组保存在一个实例属性中,并让所有定义在构造器函数的prototype属性所指对象中的方法都返回用以调用方法的那个实例的引用,那么它就具有了进行链式调用的能力。
先把$函数该为一个工厂方法,它负责创建支持链式调用的对象。
*/
(function () {
// use a provate class
function _$(els) {
this.elements = [];
for (var i = 0, len = els.length; i < len; i++) {
var element = els[i];
if (typeof element === 'string') {
element = document.getElementById(element);
}
this.elements.push(element);
}
}
_$.prototype = {
each: function (fn) {
for (var i = 0, len = this.elements.length; i < len; i++) {
fn.call(this, this.elements[i]);
}
return this;
},
setStyle: function (prop, val) {
this.each(function (el) {
if (typeof prop === 'object') {
for (var i in prop) {
if (!prop.hasOwnProperty(i)) {
continue;
}
el.style[i] = prop[i];
}
} else {
el.style[prop] = val;
}
});
return this;
},
show: function () {
var that = this;
this.each(function () {
that.setStyle('display', 'block');
});
return this;
},
addEvent: function (type, fn) {
var add = function (el) {
if (window.addEventListener) {
el.addEventListener(type, fn, false);
} else if (window.attachEvent) {
el["e" + type + fn] = fn;
//把事件函数赋给自定义对象属性
el[type + fn] = function () {
el["e" + type + fn](window.event);
};
el.attachEvent("on" + type, el[type + fn]);
}
};
this.each(function (el) {
add(el);
});
return this;
}
};
// the public interface remains the same
window.$ = function () {
return new _$(arguments);
};
})();
$(window).addEvent('load', function () {
$('test-1', 'test-2').show()
.setStyle('color', 'red')
.addEvent('click', function (e) {
$(this).setStyle({
backgroundColor: 'red',
color: 'green',
boxShadow: '0 0 20px rgba(0,0,0,0.5)'
});
});
});
/*----------------------
设计一个支持方法链式调用的js库
--------------------*/
// include syntactic sugar to help the development of our interface.
Function.prototype.method = function (name, fn) {
this.prototype[name] = fn;
return this;
};
(function () {
function _$(els) {
this.elements = [];
for (var i = 0; i < els.length; i++) {
var element = els[i];
if (typeof element === 'string') {
element = document.getElementById(element);
}
this.elements.push(element);
}
return this.elements;
}
/*
Events
* addEvent
* getEvent
*/
_$.method('addEvent', function (type, fn) {
//...
})
.method('getEvent', function (e) {
//...
})
/*
DOM
* addClass
* removeClass
* replaceClass
* hasClass
* getStyle
* setStyle
*/
.method('addClass', function (className) {
//...
})
.method('removeClass', function (className) {
//...
})
.method('replaceClass', function (oldClass, newClass) {
//...
})
.method('hasClass', function (className) {
//...
})
.method('getStyle', function (prop) {
//...
})
.method('setStyle', function (prop, val) {
//...
})
/*
AJAX
* load,Fetches anHTML fragment from a URL and inserts it into an element
*/
.method('load', function (url, method) {
//...
});
window.$ = function () {
return new _$(arguments);
};
})();
/*
如果某个现有的API已经定义了一个$函数,那么我们的这个库会将其改写,更好地解决方案是添加一个安装器
*/
Function.prototype.method = function (name, fn) {
//...
};
(function () {
function _$(els) {
//...
}
_$.method('addEvent', function (type, fn) {
//...
});
/*--------------------------------*/
window.installHelper = function (scope, interface) {
scope[interface] = function () {
return new _$(arguments);
};
};
/*--------------------------------*/
})();
// 使用
installHelper(window, '$');
$('example').show();
// 更复杂的例子,添加到一个事先定义好的命名空间中
// Define a namespace without overwriting it if it already exists
window.com = window.com || {};
com.example = com.example || {};
com.example.util = com.example.util || {};
installHelper(com.example.util, 'get');
(function () {
var get = com.example.util.get;
get('example').addEvent('click', function (e) {
get(this).addClass('hello');
})
})();
// 使用回调从支持链式调用的方法获取数据
/*
对于取值器方法,你可能会希望它们返回你要的数据而不是返回this。不过,如果你把链式调用作为首要目标,希望所有方法的使用方式保持一致的话,那么你可以利用回调技术来返回所要的数据。
*/
// API1类使用了普通的取值器(它中断了调用链)
// API2类则使用了回调方法
window.API = window.API || function () {
var name = 'hellow world';
// privileged mutator method
this.setName = function (newName) {
name = newName;
return this;
};
// privileged accessor method
this.getName = function () {
return name;
};
};
// implementation code
var o = new API;
console.log(o.getName()); // hellow world
console.log(o.setName('Meow').getName()); //Meow
// acessor with function callbacks
window.API2 = window.API2 || function () {
var name = 'hello world';
this.setName = function (newName) {
name = newName;
return this;
};
this.getName = function (callback) {
callback.call(this, name);
return this;
};
};
// implementation code
var o2 = new API2;
o2.getName(console.log).setName('Meow').getName(console.log);