资讯专栏INFORMATION COLUMN

JS 笔试手写代码

baiy / 3234人阅读

摘要:版本假设字符每个字符编码小于等于为字符串长度。将数组的元素依次传入一个函数中执行,然后把它们的返回值组成一个新数组返回。复制代码造轮子的同学注意数组的空元素不会再上述方法中遍历出来。复制代码方法,对数组进行洗牌。

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-zd])([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换行。不过需要注意的是,它不是为每个字符串都插入字样,而是相当于在组成文本节点的报语法中的每个字符后插入字样。如果是aabbcc,返回aabbcc,在opear浏览器上,默认的css不会添加上wbr样式,需要在css上添加,wbr:after{content:"00200B"}解决此问题

function wbr (target){

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

复制代码
当然,如果浏览器支持原生的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(/^ss*/, "").replace(/ss*$/, "");
}

复制代码
版本2

利用两个候选操作符链接两个正则,这样做就失去了浏览器优化的机会。由于看起来很优雅,很多类库都使用它。如jQuery,mootools

function trim(str){

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

复制代码
版本3

更好的改进版

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

复制代码
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= 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/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
}

复制代码
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: "ffffd"
    length: 4
});
console.log(array) //=> ["aaa","bbb","ccc","ffffd"]

复制代码
更常用的操作是转换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-form...

若要修正它的传参,可能是个庞大的工程。并且影响Object.prototype.toString的类型的判断。因此,不建议修正。es5.js中的源码,可以参考

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

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方法太长,此处略。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96861.html

相关文章

  • 记录一下自己的春招,唯品会、360、京东offer已收、腾讯offer_call已达!!!

    摘要:春招结果五月份了,春招已经接近尾声,因为到了周五晚上刚好有空,所以简单地记录一下自己的春招过程。我的春招从二月初一直持续到四月底,截止今天,已经斩获唯品会电商前端研发部大数据与威胁分析事业部京东精锐暑假实习生的腾讯的是早上打过来的。 春招结果 五月份了,春招已经接近尾声,因为到了周五晚上刚好有空,所以简单地记录一下自己的春招过程。我的春招从二月初一直持续到四月底,截止今天,已经斩获唯品...

    freewolf 评论0 收藏1
  • 前端笔试手写代码

    摘要:扁平化嵌套数组实现描述将嵌套多层的数组展开平铺成只有一层的数组。方法一知识点方法二知识点方法三知识点展开语法其它方法数组去重描述将数组中重复的元素过滤掉。对于函数防抖,门外有人频繁敲门,门卫按最后一次敲门来决定是否开门。知识点发布订阅模式 1. 扁平化嵌套数组/flat实现 描述:将嵌套多层的数组展开平铺成只有一层的数组。 let array = [1, [1, 2, 3], [1, ...

    zhichangterry 评论0 收藏0
  • 一篇字节跳动前端面经

    摘要:为了避免它,只需分配将要使用的必要构造函数。示例对于此示例,就需要保持父构造函数继续正常工作。结论手动设置或更新构造函数可能会导致不同且有时令人困惑的后果。为了防止它,只需在每个特定情况下定义构造函数的角色。 hr小姐姐说一共有1轮笔试 + 3轮技术面 + 1轮hr面,面试地点在中关村天使大厦,岗位是1-3年前端 笔试 笔试分为多选 简答 判断 手写代码四部分,下面只写了印象比较深的几...

    caige 评论0 收藏0
  • 我的春招求职经验分享(已拿阿里京东网易等 5 个 offer)

    摘要:面经因为我完全没有面试经验,从来没有经历过面试,于是想着在去这类大公司面试之前先找成都的小公司练练手,积累点面试经验。于是三月份开始就有成都的小公司开始约我面试。 前序 从我高考成绩出来那一刻开始,从我在高考志愿上填上计算机科学与技术这几个当时在心中堪称神圣的几个字开始,我就已经把进入中国互联网最高殿堂BAT作为我整个大学奋斗的目标,哪怕我就读的是一所位于内陆的双非一本大学我也认为我能...

    Winer 评论0 收藏1
  • 面试题里的那些各种手写

    摘要:最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现,。后面以这道题为引线面试官可能会追问什么是执行上下文的判断,的区别手写一个函数实现斐波那契数列首先拷一个阮神在他教程里的一个写法。 最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现bind,promise。手写ajax,手写一些算法。翻阅了很多书籍和博客。 这里做一个总结改进,算是对我后面大概为期一个月找...

    wh469012917 评论0 收藏0

发表评论

0条评论

baiy

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<