contains方法:判定一個字串是否包含另一個字串。常規思維,使用正則,但每次都要用new RegExp來構造,太麻煩,效能太差。轉而使用原生字串方法。如indexOf , lastIdexOf , search
function contains (target, it) {
return target.lastIndexOf(it) != -1; //indexOf改成search, lastIndexOf也可以
}
複製程式碼
在mootools版中,我們看到它支援更多的引數,目的是判定一個元素的className是否包含某個特定的class。眾所周知,元素可以新增多個class,中間以空格隔開,使用mootoos的contains就很方便檢測包含關係了。
function contains (target, str, separator) {
return separator ? ( separator + target + separator).indexOf(separator + str +separator) > -1 : target.indexOf(str) > -1;
}
複製程式碼
repeat方法: 將一個字串重複n次,如repeat("ruby",2)得到rubyruby
版本1:利用空陣列的join方法
function repeat (target, n) {
return (new Array(n + 1)).join(target);
}
複製程式碼
版本2.....6...
版本7,遞迴在瀏覽器下做了優化 ,包括ie6,屬於最好的實現方式之一
function repeat (target, n) {
if (n == 1){
return target
}
var s = repeat(target, Math.floor(n/2));
s += s;
if (n % 2) {
s += target;
}
return s;
}
複製程式碼
**byteLen方法:取得一個字串所有位元組的長度。**這是一個後端轉過來的方法。在前端,我們要使用者填寫文字,需要位元組有限制。
版本1:假設字元每個字元Unicode編碼小於等於255,byteLength為字串長度。再遍歷字串,遇到unicode編碼大於255時,為byteLength加1
function byteLen (target) {
var byteLength = target.length,
i = 0;
for ( ; i < target.length ; i++) {
if (target.charCodeAt(i) > 255) {
byteLength++;
}
}
return byteLength;
}
複製程式碼
truncate方法,用於對字串進行截斷處理,當超過限定長度,預設新增三個點號等
function truncate (target, length , truncation) {
length = length || 30;
truncation = truncation === void(0) ? '...' : truncation;
return target.length > length ? target.slice(0, length - truncation.length) + truncation : String(target);
}
複製程式碼
camelize方法,轉換為駝峰命名風格
function camelize (target) {
if (target.indexOf('_') < 0 && target.indexOf('_') < 0) {
return target; //提前判斷,提高響應效率 }
return target.replace(/[-_][^-_]/g , function(match){
return match.charCodeAt(1).toUpperCase();
})
}
複製程式碼
underscored方法。轉換為下劃線風格
function underscored(target){
return target.replace(/([a-z\d])([A-Z])/g , '$1_$2').replace(/\-/g , '_').toLowerCase();
}
複製程式碼
dasherize方法,轉換為連字元風格,亦指css變數風格(承上面的方法)
function dasherize(target){
return underscored(target).replace(/_/g, '-');
}
複製程式碼
capitalize方法,首字母大寫
function capitalize(target) {
return target.charAt(0).toUpperCase() + target.substring(1).toLowerCase();
}
複製程式碼
stripTags方法,移除字元中的html標籤。但有一個缺陷,如果其中有script標籤,會把不該顯示的指令碼也顯示出來。
function stripTags(target){
return String(target || "") .replace(/<[^>]+>/g, '');
}
複製程式碼
escapeHTML和unescapeHTML略
escapeRegExp方法:將字串安全格式轉換為正規表示式的原始碼
function escapeRegExp(target){
return target.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
}
複製程式碼
** pad方法**,與trim方法相反,pad可以為字串的某一端新增字串。常見的是在日曆的月份前面加補0,因此,被稱為fillZero。
function pad(target,n){
var zero = new Array(n).join('0');
var str = zero + target;
var resulet = str.substr(-n);
return resulet;
}
複製程式碼
高階方法,也是mass Framework使用的版本,支援更多的引數。允許從左邊或從右填充。以及使用什麼內容進行填充。
function pad (target, n, filling, right, radix){
var num = target.toString(radix || 10);
filling = filling || "0";
while (num.length < n){
if(!right){
num = filling + num;
} else {
num += filling;
}
return num;
}
}
複製程式碼
wbr方法,為目標字串新增wbr換行。不過需要注意的是,它不是為每個字串都插入
function wbr (target){
return String(target).replace(/(?:<[^>]+>) | (?:&#?[0-9a-z]{2,6};) | (.{1})/gi,'$&<wbr>').replace(/><wbr>/g,'>');
}
複製程式碼
format方法,在c語言中,有一個printf方法,我們可以在後面新增不同的型別的引數嵌入到將要輸出的字串中。這是非常有用的方法,因為在javascript中涉及到大量這樣字串拼接的工作 ,如果涉及邏輯,我們可以用模板,如果輕量點,我們可以用這個方法。
在不同的框架中名字不同,prototype.js 叫interpolate,Base2叫format,mootools叫substitute。
function format (str, object){
var array = Array.prototype.slice.call(arguments, 1);
return str.replace(/\\?\#{([^{}]+)\}/gm,function(match, name) {
if(match.charAt(0) == '\\')
return match.slice(1);
var index = Number(name)
if(index >= 0)
return array[index];
if (object && object[name] !== void 0)
return object[name];
return '';
});
}
var a = format("resulet is #{0}, #{1}",22,33)
console.log(a) // resulet is 22, 33var b = format ( "#{name} is a #{sex} #{am}" ,{
name:"wangjie",
sex:"man",
am:"111"
});
console.log(b) // wangjie is a man 111
複製程式碼
它支援兩種傳參方法,如果字串的佔位符為0,1,2這樣的非零整數,要求傳入兩個或以上的引數,否則就傳入一個物件,鍵名為佔位符。
quote方法,在字串的兩端加上雙引號。然後內部需要轉義的地方都要轉義。用於接裝JSON的鍵名或模析系統中
//code.google.com/jQuery-jsonvar escapeable = /["\\\x00-\x1f\x7f-\x9f]/g,
meta = {
'\b':'\\b',
'\t':'\\t',
'\n':'\\n',
'\f':'\\f',
'\r':'\\r',
'"':'\\"',
'\\':'\\\\'
};
function quote(target){
if (target.match(escapeable)){
return '"' + target.replace(escapeable,function(a) {
var c = meta[a];
if(typeof c === 'string') {
return c;
}
return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
}) + '"';
}
return '"' + target + '"';
}
複製程式碼
當然,如果瀏覽器支援原生的JSON,我們直接用JSON.stringify就行了,另外,FF在JSON發明之前,就支援String.prototype.quote與String.quote方法了,我們使用quote之前判定瀏覽器是否內建這些方法
字串好像沒有打的瀏覽器相容問題,有的話是IE6,IE7不支援用陣列中括號取它的每一個字元,需要用charAt來取。IE678不支援垂直分表符,因此有如下hack
var isIe678 = !+"\v1";
複製程式碼
修復舊版本IE中的trim函式。這是一個很常用的操作,通常我們需要把表單的兩側空白清除掉
版本1,用了兩次正則,實際速度非常驚人,主要得益於瀏覽器的內部優化。base2使用這種優化,引起了其它瀏覽器的跟風。於是正則的實現再也比不過字串方法了。一個著名的例子,字串拼接。直接相加比Array做成的StringBuffer還快。而StringBuffer技術在早些年備受推崇。
function trim(str){
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
複製程式碼
版本2
利用兩個候選操作符連結兩個正則,這樣做就失去了瀏覽器優化的機會。由於看起來很優雅,很多類庫都使用它。如jQuery,mootools
function trim(str){
return str.replace(/^\s+|\s+$/g, '');
}
複製程式碼
版本3
更好的改進版
function trim(str){
var str = str.replace(/^\s\s*/, ""),
ws = /\s/,
i = str.length;
while (ws.test(str.charAt(--i)))
return str.slice(0, i + 1);
}
複製程式碼
2.陣列的擴充套件與修復
得益於Prototype.js的ruby式陣列方法的侵略,讓jser()的前端工程師大開眼界,原來對陣列的操作是如此的豐富多彩。原來的javascript陣列方法就是基於棧與佇列的一套方法,像splice()還是很晚的時候加入的。讓我們回顧一下用法。
**pop()**方法,出棧操作,刪除並返回陣列的最後一個元素
**push()**方法,出棧操作,向陣列的末尾新增一個或更多元素,並返回新的長度。
**shift()**方法,出隊操作,刪除並返回陣列的第一個元素
**unshift()**方法,入隊操作,向陣列的開頭新增一個或更多的元素,返回新的長度
**slice()**方法,切片操作,從陣列中分離出一個子陣列,功能類似字串的substring、slice、substr這三兄弟。此方法也常用於轉換類陣列物件為真正的陣列
**sort()**方法,對陣列元素進行排序,有一個可選引數,為比較函式。
**reverse()**方法,顛倒陣列中元素的順序。
**splice()**方法,用於用於同時對原陣列進行增刪操作,陣列的remove方法就是基於它而寫的
**concat()**方法,用於把原陣列與引數合併成一個新陣列,如果引數為陣列,那麼它會把其第一維的元素放入新的陣列中。因此我們可以利用它實現陣列的平坦化操作和克隆操作。
**join()**方法,把陣列的所有元素放入一個字串。元素通過指定的分隔符進行分隔。你可以想象成字串的split的反操作。
在ecma262v5中,它把標準瀏覽器早已經實現的幾個方法進行了入戶處理。從此,我們可以安心的使用forEach()方法,不用擔心他們被廢棄掉了。
**indexOf()**方法,定位操作。同上,不是從後遍歷。索引操作可以說是字串的同名方法的翻版,存在就返回非負操作。不存在就返回-1.
**forEach()**方法。迭代操作。將陣列的元素依次傳入一個函式中執行,prototype.js對應的名字為 each。
**map()**方法,收集操作。將陣列的元素依次傳入一個函式中執行,然後把它們的返回值組成一個新陣列返回。prototype.js對應的名字為collect.
**fliter()**方法。過濾操作,將陣列的元素依次傳入一個函式中執行,然後把返回值為true的那個元素放入新的陣列中返回。prototype.js中它有三個名字,select、filter、findAll
**some()**方法,只要陣列中有一個元素滿足條件(放進函式返回true)。那麼它就返回true.prototype.js對應的名字為any
**every()**方法,只有陣列的元素滿足調條件(放進給定函式返回true)它才返回true。prototype.js對應的名字為any
**reduce()**方法,歸化操作。將陣列中的元素歸化為一個簡單的數值。prototype.js對應的名字為inject
**reduceRight()**方法:歸化操作,將陣列中的元素歸化為一個簡單的數值。同上,不過是從後遍歷。
由於許多擴充套件也基於這些新的標準方法,因此我們先給出IE678相容方案,全部在陣列原型上修復他們。
Array.prototype.indexOf = function(item, index){
var n = this.length, i = ~~index;
if(i<0)
i += n;
for(; i<n; i++)
if(this[i] === item)
return i;
return -1;
}
Array.prototype.lastIndexOf = function(ietm, index){
var n = this.length,
i = index == null ? n - 1 : index;
if (i < 0)
i = Math.max(0, n + 1);
for (; i >= 0; i--)
if (this[i] === item)
return i;
return -1;
}
複製程式碼
像forEach map filter some every這幾個方法,在結構上非常相似,我們可以這樣生成他們。
function iterator (vars, body, ret){
var fun = 'for (var ' + vars + 'i = 0,n = this.length; i < n; i++ ){' +
body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))')
+ '}' + ret
return Function ("fn,scope", fun);
}
Array.prototype.forEach = iterator('','_','');
Array.prototype.filter = iterator('r=[],j=0', 'if(_)r[j++]=this[i]', 'return r');
Array.prototype.map = iterator('r=[],', 'r[i]=_' , 'return r');
Array.prototype.some = iterator('','if(_)return true', 'return false');
Array.prototype.every = iterator('','if(!_)return false','return true')
複製程式碼
造輪子的同學注意:陣列的空元素不會再上述方法中遍歷出來。
[1,2,,4].forEach(function(e){
console.log(e)
})
複製程式碼
接下來,我們將對陣列的擴充套件進行總結,除去上述的
prototype.js的陣列擴充套件:eachSlice , detect , grep , include , inGruopsOf , invoke, max, min, partition, pluck , reject, sortBy , zip , size, clear, first, last, compact, faltten , without, uniq , intersect, clone, inspect
Right.js 的陣列擴充套件有 include, clean , clone , conpact, empty , first , flatten , includes, last, max , merge , min , random, reject, shuffle , size , sortBy, sum , uniq ,walk, without
mootools的陣列擴充套件,clean, clone , associate , link , contains, append, getLast , getRandom, include, combine, erase, empty, flatten, min, max, mean, sum, erase, insert.
Ext的陣列擴充套件, contains, pluck, clean, qunique, form , remove, include, merge,insersect, difference, flatten, min , max, mean, sum, erase, insert
Underscore.js 的陣列擴充套件, detect, reject, invoke , pluck, sortBy, groupBy, sortedIndex, first, last, compact, flatten,without , union, intersection , difference, quiq, zip
qooxdoo陣列擴充套件, insertAfter, insertAt, insertBefore , max , min , remove , removeAll , removeAt, sum , unique.
百度七巧板擴充套件, contains ,empty , find , remove , removeAt , unique
我們可以發現,Prototype.js 的一套方法影響深遠, 許多庫都有它的影子。我們可以根據需要與框架宗旨指定自己的陣列擴充套件, 我們在這些方面考慮如下: 至少包含**:平坦化**,** 去重**, 亂序,移除這幾個操作,其次是兩個集合間的操作,如取並集。差集,交集。 下面是各種具體實現。
contains方法, 判定陣列是否包含指定的目標
function contains(target, item){
return target.indexOf(item) > -1
}
複製程式碼
removeAt方法,移除陣列中指定位置的元素,返回布林表示成功與否
function removeAt(target, index){
return !!target.splice(index, 1).length
}
複製程式碼
remove方法,移除陣列中第一個匹配傳參的那個元素,返回布林表示成功與否。
function remove(target, ietm){
var index = target.indexOf(ietm);
if(~index)
return removeAt(target,index);
returnfalse;
}
複製程式碼
**shuffle方法,對陣列進行洗牌。**若不影響原陣列,可以先拷貝一份出來操作,有關洗牌,可以參考 博文:http://bost.ocks.org/mike/shuffle/
function shuffle(target){
var j, x, i = target.length;
for(; i > 0; j = parseInt(Math.random() * i) ,
x = target[--i],target[i] = target[j] = x) {
}
return target
}
複製程式碼
random方法,從陣列中隨機選出一個元素出來
function random(target){
return target[Math.floor(Math.random() * target.length)];
}
複製程式碼
flatten方法,對陣列進行平坦化處理,返回一個一維的新陣列
function flatten(target){
var resulet = [];
target.forEach(function(item){
if (Array.isArray(item)) {
resulet = resulet.concat(flatten(item));
} else{
resulet.push(ietm)
}
});
return resulet;
}
複製程式碼
unique方法,對陣列去重,返回一個沒有重複元素的新陣列
function unique(target){
var resulet = [];
loop: for (var i = 0, n = target.length; i < n; i++){
for (var x = i + 1; x < n; x++){
if (target[x] === target[i])
continue loop;
}
resulet.push(target[i]);
}
return resulet;
}
複製程式碼
compact方法,過濾陣列中的null和undefined,但不影響陣列。
function compact (target) {
return target.filter(function(el){
return el != null;
});
}
複製程式碼
pluck方法,獲得物件陣列中的每個元素的指定屬性,組成陣列返回
function pluck(target, name){
var resulet = [], prop;
target.forEach(function(ietm){
prop = ietm[name];
if (prop != null)
resulet.push(prop);
});
return resulet;
}
複製程式碼
groupBy方法:根據指定條件(如回撥或物件的某個屬性)進行分組,構成物件返回
function groupBy(target, val){
var resulet = {};
var iterator = $.isFunction(val) ? val : function (obj){
return obj[val];
};
target.forEach(function(value, index){
var key = iterator (value, index);
(resulet[key] || resulet[key] = [])).push(value);
});
return resulet
}
複製程式碼
sortBy方法:根據指定條件進行排序,通常用於物件陣列
function sortBy(target, fn, scope){
var array = target.map(function(ietm, index){
return {
el: ietm,
re: fn.call(scope, ietm, index)
};
}).sort(function(left, right){
var a = left.re, b = right.re;
return a < b ? -1 : a > b ? 1: 0;
})
return pluck(array, 'el');
}
複製程式碼
union方法,對兩個陣列取並集
function union (target, array){
return unique(target.concat(array));
}
複製程式碼
intersect方法:對兩個陣列取交集
function intersect(target, array){
return target.filter(function(n){
return ~array.indexOf(n);
});
}
複製程式碼
diff方法,對兩個陣列取差集
function diff(target, array){
var resulet = target.slice();
for (var i = 0, i < resulet.length; j++) {
for (var j = 0; j < resulet.length; j++) {
if (resulet[i] === array[j]){
resulet.splice(i, 1);
i--;
break;
}
}
}
return resulet;
}
複製程式碼
min方法,返回陣列的最小值,用於數字陣列
function min (target){
return Math.min.apply(0, target);
}
複製程式碼
max方法,返回陣列中的最大值。用於數字陣列
function max(target){
return Math.max.apply(0, target);
}
複製程式碼
** 3.數值的擴充套件與修復**
數值沒有什麼好擴充套件的,而且javascript的數值精度問題一向臭名昭著,修復不是一兩行程式碼的事情。先看擴充套件。
Prototype.js為它新增了8個原型方法,Succ是加1,times是將回撥重複執行制定次數,toPaddingString與上面提到的字串擴充套件方法pad作用一樣,toColorPart是轉十六機制,abs,ceil,floor,是從Math中提取出來的
mootools的情況,limit是從數值限在一個閉開間中,如果大於或小於其邊界,則等於其最大值或最小值,times與prototype.js用法相似,round是Math.round的增強版,新增了精度控制,toFloat,toInt是從window中偷學來的。
看看mass Framework對數字的擴充套件
limit方法,確保數值在[n1,n2]閉區間之內,如果超出界限,則置換為離它最近的最大值或最小值。
function limit(target, n1, n2){
var a = [n1, n2].sort();
if (target < a[0])
target = a[0];
if(target > a[1])
target = a[1];
return target;
}
複製程式碼
nearer方法,求出距離數值最近的那個數。
function nearer(target, n1, n2){
var diff1 = Math.abs(target - n1),
diff2 = Math.abs(target - n2);
return diff1 < diff2 ? n1 : n2
}
複製程式碼
Number下唯一要修復的方法是toFixed,它是用來校正精確度,最後的那個數會四捨五入操作,但在一些瀏覽器中,並未這麼操作。 簡單修復可以這樣處理
if(0.9.toFixed(0) !== '1') {
Number.Prototype.toFixed = function(n) {
var power = Math.pow(10, n);
var fixed = (Math.round(this*power)/power).toString();
if(n == 0)
return fixed;
if(fixed.indexOf('.') < 0)
fixed += '.';
var padding = n + 1 (fixed.length - fixed.indexOf('.'));
for (var i = 0; i < padding; i++)
fixed += '0';
return fixed;
}
}
複製程式碼
關於javascript誤差運算不在這裡呈現了,但在工作中,我們儘量避免小數操作與大數操作,或者轉交後臺處理,實在避免不了,可以引入專業的庫來實現。
4.函式的擴充套件與修復
V5對函式的唯一擴充套件就是bind函式,眾所周知,這來自prototype.js,此外,其它重要的函式都來自prototype.js
prototype.js的函式擴充套件如下:
argumentNames:取得函式的形參,以字串形式返回,這隻要用於其類工廠的方法鏈設計
bind,不用多描述,劫持作用域,並預先新增更多的引數。
bindAsEventListener 如bind相似,但強制返回函式的第一個引數為事件物件,這是用於修復IE多投事件API與標準API的差異。
curry 函式柯里化,用於一個操作分成多步進行,並可以改變原函式的行為。
wrap:AOP 的實現。
delay:setTimeout的偷懶寫法。
defer:強制延遲0.01秒才執行原函式
methodize:將一個函式變成其呼叫物件的方法,這也是為其類工廠的方法服務。
首先我們看bind方法,它用到了著名的閉包(所謂閉包,就是引用著外部變數的內部函式),比如下面這個函式
[
](javascript:void(0);)var observable = function(val){
var cur = val; //一個內部變數function field(neo) {
if (arguments.length){ //setterif (cur !== neo) {
cur = neo
}
} else { //getterreturn cur;
}
}
field();
return field;
}
複製程式碼
它裡邊的field函式將於外部的cur構成一個閉包。prototype.js中的bind方法只要依仗原來=函式與經過切片話的args構成閉包,而這個方法就是名副其實的curry柯里化,使用者最初的那個傳參,劫持到返回函式修正this的指向。
Function.Prototype.bind = function(context) {
if (arguments.length < 2 && context == void 0)
returnthis;
var _method = this, args = [].slice.call(arguments, 1);
returnfunction() {
return _method.apply(context, args.context.apply(args, arguments));
}
}
複製程式碼
因為有這個原型擴充套件,我們才可以修復ie的多投事件API attachEvent回到的this問題,它總是指向window物件,而標準的瀏覽器的的addEventListener中的this則其呼叫物件
var addEvent = document.addEventListener ?
function (el, type, fn, capture){
el.addEventListener (type, fn, capture)
} :
function (el, type, fn) {
el.attachEvent("on" + type, fn.bind(el, event))
}
複製程式碼
ECMA62 V5對其認證以後,唯一的增強是對呼叫者進行監測,確保它是一個函式。順便總結一下這三個東西。
call是obj.method(a, b, c)到method(obj,[a, b, c])的變換,它要求第二個引數必須存在,一定是陣列或Arguments這樣的類陣列,NodeList這樣具有爭議性的東西就不能傳入進去! 因此,jQuery對兩個陣列或類陣列的合併就使用jQuery.merge,放棄使用Array.prototype.push.apply。
bind就是apply的變種,保證返回值是一個函式。
這三個方法非常有用,我們設法將其還原出來。
var bind = function(bind) {
return {
bind: bind.bind(bind),
call: bind.bind(bind.call),
apply: bind.bind(bind.apply)
}
} (Function.Prototype.bind)
複製程式碼
那麼怎麼使用它們呢,比如我們想合併兩個陣列,直接呼叫concat方法:
var concat = bind.apply([].concat);
var a = [1, [2,3], 4];
var b = [1, 3];
複製程式碼
使用bind.bind方法可以將它們的結果進一步平坦化
var concat = bind.apply([].concat);
console.log(concat(b,a)); //=> [1,3,1,2,3,4]
複製程式碼
又如切片化操作,它經常用於轉換類陣列物件為純陣列
var slice = bind([].slice)
var array = slice({
0: "aaa",
1: "bbb",
2: "ccc",
3: "ddd"
length: 4
});
console.log(array) //=> ["aaa","bbb","ccc","ddd"]
複製程式碼
更常用的操作是轉換arguments物件,目的是為了使用陣列的一系列方法
function test() {
var args = slice(arguments)
console.log(args)//=> [1,2,3,4,5] }
test(1, 2, 3, 4, 5)
複製程式碼
我們可以將hasOwnProperty提取出來,判定物件是否在本地就擁有某屬性
var hasOwn = bind.call(Object.Prototype.hasOwnProperty);
hasOwn([],"xx") //false//使用bind.bind就需要多執行一次var hasOwn2 = bind.bind(Object.Prototype.hasOwnProperty);
hasOwn2([],"xx")() //false
複製程式碼
上面的bind.bind的行為就是一種curry,它給了你一種傳參的機會,這樣你就可以在內部判定引數的個數,決定是否繼續返回函式還是結果。這在設計計算器的連續運算上非常有用。從此角度,我們可以得到一條資訊,bind著重作用域的劫持,curry在於引數的不斷補充。
我們可以編寫一個curry,當所有步驟輸入的引數個數等於最初定義時的函式形參個數,就執行它。
function curry(fn) {
function inner(len, arg) {
if (len == 0)
return fn.apply(null, arg);
returnfunction(x) {
return inner(len - 1, arg.concat(x));
};
}
return inner(fn.length, []);
}
function sum(x, y, z, w){
return x + y + z + w;
}
curry(sum)('a')('b')('c')('d'); //=> 'abcd'
複製程式碼
不過這裡我們假定了使用者每次都只傳入一個引數,我們可以改進下
function curry2(fn) {
function inner (len, arg){
if (len <= 0)
return fn.apply(null, arg);
returnfunction() {
return inner (len - arguments.length,
arg.concat(Array.apply([],arguments)));
}
}
return inner(fn.length, []);
}
複製程式碼
這樣,我們就可以在中途傳遞多個引數,或不傳遞引數
curry2(sum)('a')('b','c')('d'); //=> 'abcd'
curry2(sum)('a')()('b','c')()('d'); //=> 'abcd'
複製程式碼
不過,上面的函式形式有個更帥氣的名稱,叫self-curry,或recurry.它強調的是遞迴自身來補全引數。 與curry相似的partial。curry的不足 引數總是通過push的方式來補全,而partial則是在定義時所有引數都已經有了,但某些位置上的引數值是個佔位符,我們在接下來的傳參只是替換掉它們。部落格上專門有《partial application in javascript》來介紹這個內容。
Function.Prototype.partial = function(){
var fn = this, args = Array.prototype.slice.call(arguments);
returnfunction() {
var arg = 0;
for (var i = 0 ; i < args.length && arg < arguments.length; i++)
if (args[i] === undefined)
args[i] = arguments[args++];
return fn.apply(this, args);
}
}
複製程式碼
它是使用undefined作為佔位符的。
var delay = setTimeout.partial(undefined, 10);
//接下來的工作就代替掉第一個引數
delay(function() {
console.log("A call to this function will be temporarily delayed")
})
複製程式碼
curry、partial應用場景在前端世界應用的並不多,前端講究的是即時顯示,許多API都是同步的,後端由於IO操作等耗時夠長,像node.js提供了大量的非同步函式來提高效能,防止堵塞。但是過多的非同步函式必然帶來回撥巢狀的問題。因此,我們需要curry等函式變換。將巢狀減少到可以接受的程度。在ajax中會講述他們的使用辦法。
**函式的修復,這涉及到方法,apply和call,這兩個方法的本質就是生成一個新的函式。**將原函式與用於的傳參放到裡面執行而已,在javascript中創造一個函式有很多辦法,常見的有函式宣告和函式表示式,次之是函式構造器,再次之是eval,setTimeout....
5.日期的擴充套件與修復
Date構造器是javascript中傳參最豐富的構造器。大致分為四種:
new Date();
new Date(value); //傳入毫秒鼠new Date(dateString)
new Date(year, month, day/*, hour, minute, second, minllisecond*/)
複製程式碼
其中,第三種可以玩N多花樣,個人建議只使用"2009/07/12 12:34:56",後面的分秒可以省略。這個所有的瀏覽器都支援,此構造器的相容列表可見此文。
http://dygraphs.com/date-formats.html
若要修正它的傳參,可能是個龐大的工程。並且影響Object.prototype.toString的型別的判斷。因此,不建議修正。es5.js中的原始碼,可以參考
https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js
javascript的日期是抄自java.util.Date,但是Date這個類的很多方法對時區等支援不夠,且不少都過時,java程式設計師也推薦使用calnedar類代替Date類。javascript可以選擇餘地比較小。如對屬性使用了前後矛盾的偏移量。月份與小時都是基於0.月份中的天數則是基於1,而年則是從1900開始的.
接下來,我們為舊版的瀏覽器新增幾個ecma262標準化日期方法吧
if(!Date.now) {
Date.now = function(){
return + new Date;
}
}
if (!Date.prototype.toISOString) {
voidfunction() {
function pad (number) {
var r = String(number);
if (r.length === 1) {
r = '0' + r ;
}
return r
}
Date.prototype.toJSON = Date.prototype.toISOString = function() {
returnthis.getUTCFllYear()
+ '_' + pad(this.getUTCMonth() + 1)
+ '_' + pad(this.getUTCDate())
+ 'T' + pad(this.getUTCHours())
+ ':' + pad(this.getUTCMinutes())
+ ':' + pad(this.getUTCSeconds())
+ '.' + String((this.getUTCMilliseconds()/1000).toFixed(3)).clice(2, 5)
+ 'Z';
}
}();
}
複製程式碼
IE67 中,getYear, setYear方法存在bug.這個修起來比較簡單:
if ((new Date).getYear() > 1900){
Date.prototype.getYear = function(){
returnthis.getFullYear() - 1900;
};
Date.prototype.setYear = function(year){
returnthis.setFullYear(year); //+1900 };
}
複製程式碼
至於擴充套件,由於涉及本地化的原因,外國許多日期庫都需要改一改才能用。其中以dataFormat這個很有用的方法為最。先給一些常用的擴充套件。
傳入兩個Date型別的日期,求其相隔的天數
var getDatePeriod = function(start, finish){
return Math.abs(start * 1 - finish * 1) / 60 / 60 / 1000 / 24;
}
複製程式碼
傳入一個Date型別的日期,判斷它所在月的第一天。
var getFirstDateInMouth = function(date) {
returnnew Date(Date.getFullYear(), Date.getMonth(), 1);
}
複製程式碼
傳入一個Date型別的日期,判斷它所在月的最後一天。
var getLastDateInMouth = function(date) {
returnnew Date(Date.getFullYear(), Date.getMonth() + 1, 0);
}
複製程式碼
判斷是否是閏年
Date.prototype.isLeapYear = function(){
returnnew Date(this.getFullYear(),2 ,0).getDate() == 29;
}
複製程式碼
獲得當前月份的天數
function getDaysInMotn1(date) {
switch (Date.getMonth()) {
case 0:
case 2:
case 4:
case 6:
case 7:
case 9:
case 11:
return 31;
case 1:
var y = Date.getFullYear();
return y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 29 : 28;
default :
return 30;
}
}
function getDaysInMonth2(date) {
returnnew Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
}
複製程式碼
dateFormat方法太長,此處略。