阅读 72

前端手写系列(四)

手写 柯里化函数

什么是柯里化

把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回 接受余下的参数而且返回结果新函数的技术。

核心思想:把多参数传入的函数拆成单参数函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。

// 简单使用

function add(x, y) {
    return x + y
}

// Currying后
function curryingAdd(x) {
    return function (y) {
        return x + y
    }
}

add(1, 2)           // 3
curryingAdd(1)(2)   // 3
复制代码

柯里化的应用

参数复用

// 普通正则验证字符串
reg.test(txt);

// 函数封装
function check(reg, txt){
	return reg.test(txt);
}

check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true

// 柯里化
function curringCheck(reg) {
	return function(txt){
    	return reg.test(txt);
    }
}

var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false
复制代码

如果有很多地方需要校验是否有数字,或者是否是字母组成,其实就是需要将第一个参数reg进行复用,这样别的地方就可以直接调用hasNumber,hasLetter等函数。

提前确认

// 每次调用这个方法都要判断一下浏览器兼容哪个方法
var on = function(element, event, handler) {
    if (document.addEventListener) {
        if (element && event && handler) {
            element.addEventListener(event, handler, false);
        }
    } else {
        if (element && event && handler) {
            element.attachEvent('on' + event, handler);
        }
    }
}

// 自执行先确定会用哪一种方法
var on = (function() {
    if (document.addEventListener) {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            }
        };
    } else {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.attachEvent('on' + event, handler);
            }
        };
    }
})();
复制代码

在做项目的过程中,经常会封装一些dom操作,第一种写法是比较常见的。第二种写法相对第一种先自执行然后再返回一个新的函数,提前确定了会走哪一个方法,不用等到使用的时候再进行判断。

延迟运行

Function.prototype.bind = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)
 
    return function() {
        return _this.apply(context, args)
    }
}
复制代码

以上是简单的bind实现,函数的主体本身不执行,而是将第一个参数设置为函数执行的上下文,其他参数依次传递给调用方法,动态创建返回一个新函数。

柯里化通用实现

// 多参数传递
function progressCurrying(fn, args) {

    var _this = this
    var len = fn.length;
    var args = args || [];

    return function() {
        // 实际调用的时候传递的参数
        // Array.prototype.slice.call可以用来将一个类数组(Array-like)对象/集合转换成一个新数组
        var _args = Array.prototype.slice.call(arguments);
        // 将数组型参数扩展成参数列表,合并两个数组
    	Array.prototype.push.apply(_args, args);

        // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数,返回函数
        if (_args.length < len) {
            return progressCurrying.call(_this, fn, _args);
        }

        // 参数收集完毕,则执行fn
        return fn.apply(this, _args);
    }
}
复制代码

柯里化的性能消耗

  • 存取arguments对象通常要比存取命名参数要慢一点
  • 一些老版本的浏览器在arguments.length的实现上是相当慢的
  • 使用fn.call 和 fn.apply通常比直接调用fn慢点
  • 创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上

但是在大部分的应用中,主要的性能瓶颈是在操作DOM节点上,这些js性能损耗基本上是可以忽略不计的,但也不能滥用。

文章分类
前端
文章标签