成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JS 筆試手寫代碼

baiy / 3372人閱讀

摘要:版本假設(shè)字符每個(gè)字符編碼小于等于為字符串長(zhǎng)度。將數(shù)組的元素依次傳入一個(gè)函數(shù)中執(zhí)行,然后把它們的返回值組成一個(gè)新數(shù)組返回。復(fù)制代碼造輪子的同學(xué)注意數(shù)組的空元素不會(huì)再上述方法中遍歷出來(lái)。復(fù)制代碼方法,對(duì)數(shù)組進(jìn)行洗牌。

contains方法:判定一個(gè)字符串是否包含另一個(gè)字符串。常規(guī)思維,使用正則,但每次都要用new RegExp來(lái)構(gòu)造,太麻煩,性能太差。轉(zhuǎn)而使用原生字符串方法。如indexOf , lastIdexOf , search

function contains (target, it) {

    return target.lastIndexOf(it) != -1; //indexOf改成search, lastIndexOf也可以
}

復(fù)制代碼
在mootools版中,我們看到它支持更多的參數(shù),目的是判定一個(gè)元素的className是否包含某個(gè)特定的class。眾所周知,元素可以添加多個(gè)class,中間以空格隔開,使用mootoos的contains就很方便檢測(cè)包含關(guān)系了。

function contains (target, str, separator) {

    return separator ? ( separator + target + separator).indexOf(separator + str +separator) > -1 : target.indexOf(str) > -1;
}

復(fù)制代碼
repeat方法: 將一個(gè)字符串重復(fù)n次,如repeat("ruby",2)得到rubyruby

版本1:利用空數(shù)組的join方法

function repeat (target, n) {

    return (new Array(n + 1)).join(target);
}

復(fù)制代碼
版本2.....6...

版本7,遞歸在瀏覽器下做了優(yōu)化 ,包括ie6,屬于最好的實(shí)現(xiàn)方式之一

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

復(fù)制代碼
byteLen方法:取得一個(gè)字符串所有字節(jié)的長(zhǎng)度。這是一個(gè)后端轉(zhuǎn)過(guò)來(lái)的方法。在前端,我們要用戶填寫文本,需要字節(jié)有限制。

版本1:假設(shè)字符每個(gè)字符Unicode編碼小于等于255,byteLength為字符串長(zhǎng)度。再遍歷字符串,遇到unicode編碼大于255時(shí),為byteLength加1

function byteLen (target) {

    var byteLength = target.length, 
        i = 0;
    for ( ; i < target.length ; i++) {
        if (target.charCodeAt(i) > 255) {
            byteLength++;
        }
    }
    return byteLength;
}

復(fù)制代碼
truncate方法,用于對(duì)字符串進(jìn)行截?cái)嗵幚?,?dāng)超過(guò)限定長(zhǎng)度,默認(rèn)添加三個(gè)點(diǎn)號(hào)等

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

復(fù)制代碼
camelize方法,轉(zhuǎn)換為駝峰命名風(fēng)格

function camelize (target) {

    if (target.indexOf("_") < 0 && target.indexOf("_") < 0) {
        return target; //提前判斷,提高響應(yīng)效率        }
    return target.replace(/[-_][^-_]/g , function(match){
        return match.charCodeAt(1).toUpperCase();
    })
}

復(fù)制代碼
underscored方法。轉(zhuǎn)換為下劃線風(fēng)格

function underscored(target){

    return target.replace(/([a-zd])([A-Z])/g , "$1_$2").replace(/-/g , "_").toLowerCase();
}

復(fù)制代碼
dasherize方法,轉(zhuǎn)換為連字符風(fēng)格,亦指css變量風(fēng)格(承上面的方法)

function dasherize(target){

    return underscored(target).replace(/_/g, "-");
}

復(fù)制代碼
capitalize方法,首字母大寫

function capitalize(target) {

    return target.charAt(0).toUpperCase() + target.substring(1).toLowerCase();
}

復(fù)制代碼
stripTags方法,移除字符中的html標(biāo)簽。但有一個(gè)缺陷,如果其中有script標(biāo)簽,會(huì)把不該顯示的腳本也顯示出來(lái)。

function stripTags(target){

    return String(target || "") .replace(/<[^>]+>/g, "");
}

復(fù)制代碼
escapeHTML和unescapeHTML略

escapeRegExp方法:將字符串安全格式轉(zhuǎn)換為正則表達(dá)式的源碼

function escapeRegExp(target){

    return target.replace(/([-.*+?^${}()|[]/])/g, "$1");
}

復(fù)制代碼
pad方法,與trim方法相反,pad可以為字符串的某一端添加字符串。常見的是在日歷的月份前面加補(bǔ)0,因此,被稱為fillZero。

function pad(target,n){

    var zero = new Array(n).join("0");
    var str = zero + target;
    var resulet = str.substr(-n);
    return resulet;
}

復(fù)制代碼
高級(jí)方法,也是mass Framework使用的版本,支持更多的參數(shù)。允許從左邊或從右填充。以及使用什么內(nèi)容進(jìn)行填充。

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

復(fù)制代碼
wbr方法,為目標(biāo)字符串添加wbr換行。不過(guò)需要注意的是,它不是為每個(gè)字符串都插入字樣,而是相當(dāng)于在組成文本節(jié)點(diǎn)的報(bào)語(yǔ)法中的每個(gè)字符后插入字樣。如果是aabbcc,返回aabbcc,在opear瀏覽器上,默認(rèn)的css不會(huì)添加上wbr樣式,需要在css上添加,wbr:after{content:"00200B"}解決此問(wèn)題

function wbr (target){

    return String(target).replace(/(?:<[^>]+>) | (?:&#?[0-9a-z]{2,6};) | (.{1})/gi,"$&").replace(/>/g,">");
}

復(fù)制代碼
format方法,在c語(yǔ)言中,有一個(gè)printf方法,我們可以在后面添加不同的類型的參數(shù)嵌入到將要輸出的字符串中。這是非常有用的方法,因?yàn)樵趈avascript中涉及到大量這樣字符串拼接的工作 ,如果涉及邏輯,我們可以用模板,如果輕量點(diǎn),我們可以用這個(gè)方法。

在不同的框架中名字不同,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

復(fù)制代碼
它支持兩種傳參方法,如果字符串的占位符為0,1,2這樣的非零整數(shù),要求傳入兩個(gè)或以上的參數(shù),否則就傳入一個(gè)對(duì)象,鍵名為占位符。

quote方法,在字符串的兩端加上雙引號(hào)。然后內(nèi)部需要轉(zhuǎn)義的地方都要轉(zhuǎn)義。用于接裝JSON的鍵名或模析系統(tǒng)中

//code.google.com/jQuery-jsonvar escapeable = /["x00-x1fx7f-x9f]/g,

                meta = {
                    "":"",
                    "	":"	",
                    "
":"
",
                    "f":"f",
                    "
":"
",
                    """:""",
                    "":""
                };
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 + """;
}               

復(fù)制代碼
當(dāng)然,如果瀏覽器支持原生的JSON,我們直接用JSON.stringify就行了,另外,F(xiàn)F在JSON發(fā)明之前,就支持String.prototype.quote與String.quote方法了,我們使用quote之前判定瀏覽器是否內(nèi)置這些方法

字符串好像沒(méi)有打的瀏覽器兼容問(wèn)題,有的話是IE6,IE7不支持用數(shù)組中括號(hào)取它的每一個(gè)字符,需要用charAt來(lái)取。IE678不支持垂直分表符,因此有如下hack

var isIe678 = !+"v1";
復(fù)制代碼
修復(fù)舊版本IE中的trim函數(shù)。這是一個(gè)很常用的操作,通常我們需要把表單的兩側(cè)空白清除掉

版本1,用了兩次正則,實(shí)際速度非常驚人,主要得益于瀏覽器的內(nèi)部?jī)?yōu)化。base2使用這種優(yōu)化,引起了其它瀏覽器的跟風(fēng)。于是正則的實(shí)現(xiàn)再也比不過(guò)字符串方法了。一個(gè)著名的例子,字符串拼接。直接相加比Array做成的StringBuffer還快。而StringBuffer技術(shù)在早些年備受推崇。

function trim(str){

    return str.replace(/^ss*/, "").replace(/ss*$/, "");
}

復(fù)制代碼
版本2

利用兩個(gè)候選操作符鏈接兩個(gè)正則,這樣做就失去了瀏覽器優(yōu)化的機(jī)會(huì)。由于看起來(lái)很優(yōu)雅,很多類庫(kù)都使用它。如jQuery,mootools

function trim(str){

    return str.replace(/^s+|s+$/g, "");
}

復(fù)制代碼
版本3

更好的改進(jìn)版

function trim(str){

    var str = str.replace(/^ss*/, ""),
            ws = /s/,
            i = str.length;
while (ws.test(str.charAt(--i)))
    return str.slice(0, i + 1);
}

復(fù)制代碼
2.數(shù)組的擴(kuò)展與修復(fù)

得益于Prototype.js的ruby式數(shù)組方法的侵略,讓jser()的前端工程師大開眼界,原來(lái)對(duì)數(shù)組的操作是如此的豐富多彩。原來(lái)的javascript數(shù)組方法就是基于棧與隊(duì)列的一套方法,像splice()還是很晚的時(shí)候加入的。讓我們回顧一下用法。

pop()方法,出棧操作,刪除并返回?cái)?shù)組的最后一個(gè)元素

push()方法,出棧操作,向數(shù)組的末尾添加一個(gè)或更多元素,并返回新的長(zhǎng)度。

shift()方法,出隊(duì)操作,刪除并返回?cái)?shù)組的第一個(gè)元素

unshift()方法,入隊(duì)操作,向數(shù)組的開頭添加一個(gè)或更多的元素,返回新的長(zhǎng)度

slice()方法,切片操作,從數(shù)組中分離出一個(gè)子數(shù)組,功能類似字符串的substring、slice、substr這三兄弟。此方法也常用于轉(zhuǎn)換類數(shù)組對(duì)象為真正的數(shù)組

sort()方法,對(duì)數(shù)組元素進(jìn)行排序,有一個(gè)可選參數(shù),為比較函數(shù)。

reverse()方法,顛倒數(shù)組中元素的順序。

splice()方法,用于用于同時(shí)對(duì)原數(shù)組進(jìn)行增刪操作,數(shù)組的remove方法就是基于它而寫的

concat()方法,用于把原數(shù)組與參數(shù)合并成一個(gè)新數(shù)組,如果參數(shù)為數(shù)組,那么它會(huì)把其第一維的元素放入新的數(shù)組中。因此我們可以利用它實(shí)現(xiàn)數(shù)組的平坦化操作和克隆操作。

join()方法,把數(shù)組的所有元素放入一個(gè)字符串。元素通過(guò)指定的分隔符進(jìn)行分隔。你可以想象成字符串的split的反操作。

在ecma262v5中,它把標(biāo)準(zhǔn)瀏覽器早已經(jīng)實(shí)現(xiàn)的幾個(gè)方法進(jìn)行了入戶處理。從此,我們可以安心的使用forEach()方法,不用擔(dān)心他們被廢棄掉了。

indexOf()方法,定位操作。同上,不是從后遍歷。索引操作可以說(shuō)是字符串的同名方法的翻版,存在就返回非負(fù)操作。不存在就返回-1.

forEach()方法。迭代操作。將數(shù)組的元素依次傳入一個(gè)函數(shù)中執(zhí)行,prototype.js對(duì)應(yīng)的名字為 each。

map()方法,收集操作。將數(shù)組的元素依次傳入一個(gè)函數(shù)中執(zhí)行,然后把它們的返回值組成一個(gè)新數(shù)組返回。prototype.js對(duì)應(yīng)的名字為collect.

fliter()方法。過(guò)濾操作,將數(shù)組的元素依次傳入一個(gè)函數(shù)中執(zhí)行,然后把返回值為true的那個(gè)元素放入新的數(shù)組中返回。prototype.js中它有三個(gè)名字,select、filter、findAll

some()方法,只要數(shù)組中有一個(gè)元素滿足條件(放進(jìn)函數(shù)返回true)。那么它就返回true.prototype.js對(duì)應(yīng)的名字為any

every()方法,只有數(shù)組的元素滿足調(diào)條件(放進(jìn)給定函數(shù)返回true)它才返回true。prototype.js對(duì)應(yīng)的名字為any

reduce()方法,歸化操作。將數(shù)組中的元素歸化為一個(gè)簡(jiǎn)單的數(shù)值。prototype.js對(duì)應(yīng)的名字為inject

reduceRight()方法:歸化操作,將數(shù)組中的元素歸化為一個(gè)簡(jiǎn)單的數(shù)值。同上,不過(guò)是從后遍歷。

由于許多擴(kuò)展也基于這些新的標(biāo)準(zhǔn)方法,因此我們先給出IE678兼容方案,全部在數(shù)組原型上修復(fù)他們。

Array.prototype.indexOf = function(item, index){
    var n = this.length, i = ~~index;
    if(i<0)
        i += n;
    for(; i= 0; i--)
    if (this[i] === item)
       return i;
return -1;
}

復(fù)制代碼
像forEach map filter some every這幾個(gè)方法,在結(jié)構(gòu)上非常相似,我們可以這樣生成他們。

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")

復(fù)制代碼
造輪子的同學(xué)注意:數(shù)組的空元素不會(huì)再上述方法中遍歷出來(lái)。

[1,2,,4].forEach(function(e){
    console.log(e)
})

復(fù)制代碼
接下來(lái),我們將對(duì)數(shù)組的擴(kuò)展進(jìn)行總結(jié),除去上述的

prototype.js的數(shù)組擴(kuò)展: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 的數(shù)組擴(kuò)展有 include, clean , clone , conpact, empty , first , flatten , includes, last, max , merge , min , random, reject, shuffle , size , sortBy, sum , uniq ,walk, without

mootools的數(shù)組擴(kuò)展,clean, clone , associate , link , contains, append, getLast , getRandom, include, combine, erase, empty, flatten, min, max, mean, sum, erase, insert.

Ext的數(shù)組擴(kuò)展, contains, pluck, clean, qunique, form , remove, include, merge,insersect, difference, flatten, min , max, mean, sum, erase, insert

Underscore.js 的數(shù)組擴(kuò)展, detect, reject, invoke , pluck, sortBy, groupBy, sortedIndex, first, last, compact, flatten,without , union, intersection , difference, quiq, zip

qooxdoo數(shù)組擴(kuò)展, insertAfter, insertAt, insertBefore , max , min , remove , removeAll , removeAt, sum , unique.

百度七巧板擴(kuò)展, contains ,empty , find , remove , removeAt , unique

我們可以發(fā)現(xiàn),Prototype.js 的一套方法影響深遠(yuǎn), 許多庫(kù)都有它的影子。我們可以根據(jù)需要與框架宗旨指定自己的數(shù)組擴(kuò)展, 我們?cè)谶@些方面考慮如下: 至少包含:平坦化, 去重, 亂序,移除這幾個(gè)操作,其次是兩個(gè)集合間的操作,如取并集。差集,交集。 下面是各種具體實(shí)現(xiàn)。

contains方法, 判定數(shù)組是否包含指定的目標(biāo)

function contains(target, item){

    return target.indexOf(item) > -1
}

復(fù)制代碼
removeAt方法,移除數(shù)組中指定位置的元素,返回布爾表示成功與否

function removeAt(target, index){

    return !!target.splice(index, 1).length
}

復(fù)制代碼
remove方法,移除數(shù)組中第一個(gè)匹配傳參的那個(gè)元素,返回布爾表示成功與否。

function remove(target, ietm){

    var index = target.indexOf(ietm);
    if(~index)
        return removeAt(target,index);
    returnfalse;
}

復(fù)制代碼
shuffle方法,對(duì)數(shù)組進(jìn)行洗牌。若不影響原數(shù)組,可以先拷貝一份出來(lái)操作,有關(guān)洗牌,可以參考 博文:http://bost.ocks.org/mike/shu...

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
}

復(fù)制代碼
random方法,從數(shù)組中隨機(jī)選出一個(gè)元素出來(lái)

function random(target){

    return target[Math.floor(Math.random() * target.length)];
}

復(fù)制代碼
flatten方法,對(duì)數(shù)組進(jìn)行平坦化處理,返回一個(gè)一維的新數(shù)組

function flatten(target){

    var resulet = [];
    target.forEach(function(item){
        if (Array.isArray(item)) {
            resulet = resulet.concat(flatten(item));
        } else{
            resulet.push(ietm)
        }
    });
    return resulet;
}

復(fù)制代碼
unique方法,對(duì)數(shù)組去重,返回一個(gè)沒(méi)有重復(fù)元素的新數(shù)組

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

復(fù)制代碼
compact方法,過(guò)濾數(shù)組中的null和undefined,但不影響數(shù)組。

function compact (target) {

    return target.filter(function(el){
        return el != null;
    });
}

復(fù)制代碼
pluck方法,獲得對(duì)象數(shù)組中的每個(gè)元素的指定屬性,組成數(shù)組返回

function pluck(target, name){

    var resulet = [], prop;
    target.forEach(function(ietm){
        prop = ietm[name];
        if (prop != null)
            resulet.push(prop);
    });
    return resulet;
}

復(fù)制代碼
groupBy方法:根據(jù)指定條件(如回調(diào)或?qū)ο蟮哪硞€(gè)屬性)進(jìn)行分組,構(gòu)成對(duì)象返回

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
}

復(fù)制代碼
sortBy方法:根據(jù)指定條件進(jìn)行排序,通常用于對(duì)象數(shù)組

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

復(fù)制代碼
union方法,對(duì)兩個(gè)數(shù)組取并集

function union (target, array){

    return unique(target.concat(array));
}

復(fù)制代碼
intersect方法:對(duì)兩個(gè)數(shù)組取交集

function intersect(target, array){

    return target.filter(function(n){
        return ~array.indexOf(n);
    });
}

復(fù)制代碼
diff方法,對(duì)兩個(gè)數(shù)組取差集

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

復(fù)制代碼
min方法,返回?cái)?shù)組的最小值,用于數(shù)字?jǐn)?shù)組

function min (target){

    return Math.min.apply(0, target);
}

復(fù)制代碼
max方法,返回?cái)?shù)組中的最大值。用于數(shù)字?jǐn)?shù)組

function max(target){

    return Math.max.apply(0, target);
}

復(fù)制代碼
3.數(shù)值的擴(kuò)展與修復(fù)

數(shù)值沒(méi)有什么好擴(kuò)展的,而且javascript的數(shù)值精度問(wèn)題一向臭名昭著,修復(fù)不是一兩行代碼的事情。先看擴(kuò)展。

Prototype.js為它添加了8個(gè)原型方法,Succ是加1,times是將回調(diào)重復(fù)執(zhí)行制定次數(shù),toPaddingString與上面提到的字符串?dāng)U展方法pad作用一樣,toColorPart是轉(zhuǎn)十六機(jī)制,abs,ceil,floor,是從Math中提取出來(lái)的

mootools的情況,limit是從數(shù)值限在一個(gè)閉開間中,如果大于或小于其邊界,則等于其最大值或最小值,times與prototype.js用法相似,round是Math.round的增強(qiáng)版,添加了精度控制,toFloat,toInt是從window中偷學(xué)來(lái)的。

看看mass Framework對(duì)數(shù)字的擴(kuò)展

limit方法,確保數(shù)值在[n1,n2]閉區(qū)間之內(nèi),如果超出界限,則置換為離它最近的最大值或最小值。

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

復(fù)制代碼
nearer方法,求出距離數(shù)值最近的那個(gè)數(shù)。

function nearer(target, n1, n2){

    var diff1 = Math.abs(target - n1),
        diff2 = Math.abs(target - n2);
    return diff1 < diff2 ? n1 : n2
}

復(fù)制代碼
Number下唯一要修復(fù)的方法是toFixed,它是用來(lái)校正精確度,最后的那個(gè)數(shù)會(huì)四舍五入操作,但在一些瀏覽器中,并未這么操作。 簡(jiǎn)單修復(fù)可以這樣處理

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

復(fù)制代碼
關(guān)于javascript誤差運(yùn)算不在這里呈現(xiàn)了,但在工作中,我們盡量避免小數(shù)操作與大數(shù)操作,或者轉(zhuǎn)交后臺(tái)處理,實(shí)在避免不了,可以引入專業(yè)的庫(kù)來(lái)實(shí)現(xiàn)。

4.函數(shù)的擴(kuò)展與修復(fù)

V5對(duì)函數(shù)的唯一擴(kuò)展就是bind函數(shù),眾所周知,這來(lái)自prototype.js,此外,其它重要的函數(shù)都來(lái)自prototype.js

prototype.js的函數(shù)擴(kuò)展如下:

argumentNames:取得函數(shù)的形參,以字符串形式返回,這只要用于其類工廠的方法鏈設(shè)計(jì)

bind,不用多描述,劫持作用域,并預(yù)先添加更多的參數(shù)。

bindAsEventListener 如bind相似,但強(qiáng)制返回函數(shù)的第一個(gè)參數(shù)為事件對(duì)象,這是用于修復(fù)IE多投事件API與標(biāo)準(zhǔn)API的差異。

curry 函數(shù)柯里化,用于一個(gè)操作分成多步進(jìn)行,并可以改變?cè)瘮?shù)的行為。

wrap:AOP 的實(shí)現(xiàn)。

delay:setTimeout的偷懶寫法。

defer:強(qiáng)制延遲0.01秒才執(zhí)行原函數(shù)

methodize:將一個(gè)函數(shù)變成其調(diào)用對(duì)象的方法,這也是為其類工廠的方法服務(wù)。

首先我們看bind方法,它用到了著名的閉包(所謂閉包,就是引用著外部變量的內(nèi)部函數(shù)),比如下面這個(gè)函數(shù)

[

復(fù)制代碼
](javascript:void(0);)
var observable = function(val){

    var cur = val; //一個(gè)內(nèi)部變量function field(neo) {
        if (arguments.length){ //setterif (cur !== neo) {
                cur = neo
            }
        } else { //getterreturn cur;
        }
    }
    field();
    return field;
}

復(fù)制代碼
它里邊的field函數(shù)將于外部的cur構(gòu)成一個(gè)閉包。prototype.js中的bind方法只要依仗原來(lái)=函數(shù)與經(jīng)過(guò)切片話的args構(gòu)成閉包,而這個(gè)方法就是名副其實(shí)的curry柯里化,用戶最初的那個(gè)傳參,劫持到返回函數(shù)修正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));
    }
}

復(fù)制代碼
因?yàn)橛羞@個(gè)原型擴(kuò)展,我們才可以修復(fù)ie的多投事件API attachEvent回到的this問(wèn)題,它總是指向window對(duì)象,而標(biāo)準(zhǔn)的瀏覽器的的addEventListener中的this則其調(diào)用對(duì)象

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

復(fù)制代碼
ECMA62 V5對(duì)其認(rèn)證以后,唯一的增強(qiáng)是對(duì)調(diào)用者進(jìn)行監(jiān)測(cè),確保它是一個(gè)函數(shù)。順便總結(jié)一下這三個(gè)東西。

call是obj.method(a, b, c)到method(obj,[a, b, c])的變換,它要求第二個(gè)參數(shù)必須存在,一定是數(shù)組或Arguments這樣的類數(shù)組,NodeList這樣具有爭(zhēng)議性的東西就不能傳入進(jìn)去! 因此,jQuery對(duì)兩個(gè)數(shù)組或類數(shù)組的合并就使用jQuery.merge,放棄使用Array.prototype.push.apply。

bind就是apply的變種,保證返回值是一個(gè)函數(shù)。

這三個(gè)方法非常有用,我們?cè)O(shè)法將其還原出來(lái)。

var bind = function(bind) {

    return {
        bind: bind.bind(bind),
        call: bind.bind(bind.call),
        apply: bind.bind(bind.apply)
    }
} (Function.Prototype.bind)

復(fù)制代碼
那么怎么使用它們呢,比如我們想合并兩個(gè)數(shù)組,直接調(diào)用concat方法:

var concat = bind.apply([].concat);

var a = [1, [2,3], 4];
var b = [1, 3];

復(fù)制代碼
使用bind.bind方法可以將它們的結(jié)果進(jìn)一步平坦化

var concat = bind.apply([].concat);

console.log(concat(b,a)); //=> [1,3,1,2,3,4]

復(fù)制代碼
又如切片化操作,它經(jīng)常用于轉(zhuǎn)換類數(shù)組對(duì)象為純數(shù)組

var slice = bind([].slice)

var array = slice({
    0: "aaa",
    1: "bbb",
    2: "ccc",
    3: "ffffd"
    length: 4
});
console.log(array) //=> ["aaa","bbb","ccc","ffffd"]

復(fù)制代碼
更常用的操作是轉(zhuǎn)換arguments對(duì)象,目的是為了使用數(shù)組的一系列方法

function test() {

    var args = slice(arguments)
    console.log(args)//=> [1,2,3,4,5]    }
test(1, 2, 3, 4, 5)

復(fù)制代碼
我們可以將hasOwnProperty提取出來(lái),判定對(duì)象是否在本地就擁有某屬性

var hasOwn = bind.call(Object.Prototype.hasOwnProperty);

hasOwn([],"xx") //false//使用bind.bind就需要多執(zhí)行一次var hasOwn2 = bind.bind(Object.Prototype.hasOwnProperty);
hasOwn2([],"xx")() //false

復(fù)制代碼
上面的bind.bind的行為就是一種curry,它給了你一種傳參的機(jī)會(huì),這樣你就可以在內(nèi)部判定參數(shù)的個(gè)數(shù),決定是否繼續(xù)返回函數(shù)還是結(jié)果。這在設(shè)計(jì)計(jì)算器的連續(xù)運(yùn)算上非常有用。從此角度,我們可以得到一條信息,bind著重作用域的劫持,curry在于參數(shù)的不斷補(bǔ)充。

我們可以編寫一個(gè)curry,當(dāng)所有步驟輸入的參數(shù)個(gè)數(shù)等于最初定義時(shí)的函數(shù)形參個(gè)數(shù),就執(zhí)行它。

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"

復(fù)制代碼
不過(guò)這里我們假定了用戶每次都只傳入一個(gè)參數(shù),我們可以改進(jìn)下

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, []);
}

復(fù)制代碼
這樣,我們就可以在中途傳遞多個(gè)參數(shù),或不傳遞參數(shù)

curry2(sum)("a")("b","c")("d"); //=> "abcd"
curry2(sum)("a")()("b","c")()("d"); //=> "abcd"
復(fù)制代碼
不過(guò),上面的函數(shù)形式有個(gè)更帥氣的名稱,叫self-curry,或recurry.它強(qiáng)調(diào)的是遞歸自身來(lái)補(bǔ)全參數(shù)。 與curry相似的partial。curry的不足 參數(shù)總是通過(guò)push的方式來(lái)補(bǔ)全,而partial則是在定義時(shí)所有參數(shù)都已經(jīng)有了,但某些位置上的參數(shù)值是個(gè)占位符,我們?cè)诮酉聛?lái)的傳參只是替換掉它們。博客上專門有《partial application in javascript》來(lái)介紹這個(gè)內(nèi)容。

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

復(fù)制代碼
它是使用undefined作為占位符的。

var delay = setTimeout.partial(undefined, 10);

//接下來(lái)的工作就代替掉第一個(gè)參數(shù)
delay(function() {
    console.log("A  call to this function will be temporarily delayed")
})

復(fù)制代碼
curry、partial應(yīng)用場(chǎng)景在前端世界應(yīng)用的并不多,前端講究的是即時(shí)顯示,許多API都是同步的,后端由于IO操作等耗時(shí)夠長(zhǎng),像node.js提供了大量的異步函數(shù)來(lái)提高性能,防止堵塞。但是過(guò)多的異步函數(shù)必然帶來(lái)回調(diào)嵌套的問(wèn)題。因此,我們需要curry等函數(shù)變換。將嵌套減少到可以接受的程度。在ajax中會(huì)講述他們的使用辦法。

函數(shù)的修復(fù),這涉及到方法,apply和call,這兩個(gè)方法的本質(zhì)就是生成一個(gè)新的函數(shù)。將原函數(shù)與用于的傳參放到里面執(zhí)行而已,在javascript中創(chuàng)造一個(gè)函數(shù)有很多辦法,常見的有函數(shù)聲明和函數(shù)表達(dá)式,次之是函數(shù)構(gòu)造器,再次之是eval,setTimeout....

5.日期的擴(kuò)展與修復(fù)

Date構(gòu)造器是javascript中傳參最豐富的構(gòu)造器。大致分為四種:

new Date();

new Date(value); //傳入毫秒鼠new Date(dateString)
new Date(year, month, day/*, hour, minute, second, minllisecond*/)

復(fù)制代碼
其中,第三種可以玩N多花樣,個(gè)人建議只使用"2009/07/12 12:34:56",后面的分秒可以省略。這個(gè)所有的瀏覽器都支持,此構(gòu)造器的兼容列表可見此文。

http://dygraphs.com/date-form...

若要修正它的傳參,可能是個(gè)龐大的工程。并且影響Object.prototype.toString的類型的判斷。因此,不建議修正。es5.js中的源碼,可以參考

https://github.com/kriskowal/...

javascript的日期是抄自java.util.Date,但是Date這個(gè)類的很多方法對(duì)時(shí)區(qū)等支持不夠,且不少都過(guò)時(shí),java程序員也推薦使用calnedar類代替Date類。javascript可以選擇余地比較小。如對(duì)屬性使用了前后矛盾的偏移量。月份與小時(shí)都是基于0.月份中的天數(shù)則是基于1,而年則是從1900開始的.

接下來(lái),我們?yōu)榕f版的瀏覽器添加幾個(gè)ecma262標(biāo)準(zhǔn)化日期方法吧

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

復(fù)制代碼
IE67 中,getYear, setYear方法存在bug.這個(gè)修起來(lái)比較簡(jiǎn)單:

if ((new Date).getYear() > 1900){

    Date.prototype.getYear = function(){
        returnthis.getFullYear() - 1900;
    };
Date.prototype.setYear = function(year){
    returnthis.setFullYear(year); //+1900    };
}

復(fù)制代碼
至于擴(kuò)展,由于涉及本地化的原因,外國(guó)許多日期庫(kù)都需要改一改才能用。其中以dataFormat這個(gè)很有用的方法為最。先給一些常用的擴(kuò)展。

傳入兩個(gè)Date類型的日期,求其相隔的天數(shù)

var getDatePeriod = function(start, finish){

    return Math.abs(start * 1 - finish * 1) / 60 / 60 / 1000 / 24;
}

復(fù)制代碼
傳入一個(gè)Date類型的日期,判斷它所在月的第一天。

var getFirstDateInMouth = function(date) {

    returnnew Date(Date.getFullYear(), Date.getMonth(), 1);
}

復(fù)制代碼
傳入一個(gè)Date類型的日期,判斷它所在月的最后一天。

var getLastDateInMouth = function(date) {

    returnnew Date(Date.getFullYear(), Date.getMonth() + 1, 0);
}

復(fù)制代碼
判斷是否是閏年

Date.prototype.isLeapYear = function(){
    returnnew Date(this.getFullYear(),2 ,0).getDate() == 29;
}

復(fù)制代碼
獲得當(dāng)前月份的天數(shù)

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

復(fù)制代碼
dateFormat方法太長(zhǎng),此處略。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/96861.html

相關(guān)文章

  • 記錄一下自己的春招,唯品會(huì)、360、京東offer已收、騰訊offer_call已達(dá)?。?!

    摘要:春招結(jié)果五月份了,春招已經(jīng)接近尾聲,因?yàn)榈搅酥芪逋砩蟿偤糜锌?,所以?jiǎn)單地記錄一下自己的春招過(guò)程。我的春招從二月初一直持續(xù)到四月底,截止今天,已經(jīng)斬獲唯品會(huì)電商前端研發(fā)部大數(shù)據(jù)與威脅分析事業(yè)部京東精銳暑假實(shí)習(xí)生的騰訊的是早上打過(guò)來(lái)的。 春招結(jié)果 五月份了,春招已經(jīng)接近尾聲,因?yàn)榈搅酥芪逋砩蟿偤糜锌?,所以?jiǎn)單地記錄一下自己的春招過(guò)程。我的春招從二月初一直持續(xù)到四月底,截止今天,已經(jīng)斬獲唯品...

    freewolf 評(píng)論0 收藏1
  • 前端筆試手寫代碼

    摘要:扁平化嵌套數(shù)組實(shí)現(xiàn)描述將嵌套多層的數(shù)組展開平鋪成只有一層的數(shù)組。方法一知識(shí)點(diǎn)方法二知識(shí)點(diǎn)方法三知識(shí)點(diǎn)展開語(yǔ)法其它方法數(shù)組去重描述將數(shù)組中重復(fù)的元素過(guò)濾掉。對(duì)于函數(shù)防抖,門外有人頻繁敲門,門衛(wèi)按最后一次敲門來(lái)決定是否開門。知識(shí)點(diǎn)發(fā)布訂閱模式 1. 扁平化嵌套數(shù)組/flat實(shí)現(xiàn) 描述:將嵌套多層的數(shù)組展開平鋪成只有一層的數(shù)組。 let array = [1, [1, 2, 3], [1, ...

    zhichangterry 評(píng)論0 收藏0
  • 一篇字節(jié)跳動(dòng)前端面經(jīng)

    摘要:為了避免它,只需分配將要使用的必要構(gòu)造函數(shù)。示例對(duì)于此示例,就需要保持父構(gòu)造函數(shù)繼續(xù)正常工作。結(jié)論手動(dòng)設(shè)置或更新構(gòu)造函數(shù)可能會(huì)導(dǎo)致不同且有時(shí)令人困惑的后果。為了防止它,只需在每個(gè)特定情況下定義構(gòu)造函數(shù)的角色。 hr小姐姐說(shuō)一共有1輪筆試 + 3輪技術(shù)面 + 1輪hr面,面試地點(diǎn)在中關(guān)村天使大廈,崗位是1-3年前端 筆試 筆試分為多選 簡(jiǎn)答 判斷 手寫代碼四部分,下面只寫了印象比較深的幾...

    caige 評(píng)論0 收藏0
  • 我的春招求職經(jīng)驗(yàn)分享(已拿阿里京東網(wǎng)易等 5 個(gè) offer)

    摘要:面經(jīng)因?yàn)槲彝耆珱](méi)有面試經(jīng)驗(yàn),從來(lái)沒(méi)有經(jīng)歷過(guò)面試,于是想著在去這類大公司面試之前先找成都的小公司練練手,積累點(diǎn)面試經(jīng)驗(yàn)。于是三月份開始就有成都的小公司開始約我面試。 前序 從我高考成績(jī)出來(lái)那一刻開始,從我在高考志愿上填上計(jì)算機(jī)科學(xué)與技術(shù)這幾個(gè)當(dāng)時(shí)在心中堪稱神圣的幾個(gè)字開始,我就已經(jīng)把進(jìn)入中國(guó)互聯(lián)網(wǎng)最高殿堂BAT作為我整個(gè)大學(xué)奮斗的目標(biāo),哪怕我就讀的是一所位于內(nèi)陸的雙非一本大學(xué)我也認(rèn)為我能...

    Winer 評(píng)論0 收藏1
  • 面試題里的那些各種手寫

    摘要:最近準(zhǔn)備初級(jí)前端面試,發(fā)現(xiàn)有很多手寫實(shí)現(xiàn)什么的,例如什么手寫實(shí)現(xiàn),。后面以這道題為引線面試官可能會(huì)追問(wèn)什么是執(zhí)行上下文的判斷,的區(qū)別手寫一個(gè)函數(shù)實(shí)現(xiàn)斐波那契數(shù)列首先拷一個(gè)阮神在他教程里的一個(gè)寫法。 最近準(zhǔn)備初級(jí)前端面試,發(fā)現(xiàn)有很多手寫實(shí)現(xiàn)什么的,例如什么手寫實(shí)現(xiàn)bind,promise。手寫ajax,手寫一些算法。翻閱了很多書籍和博客。 這里做一個(gè)總結(jié)改進(jìn),算是對(duì)我后面大概為期一個(gè)月找...

    wh469012917 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<