专栏首页Super 前端编写高质量代码:改善JavaScript程序建议--函数式编程

编写高质量代码:改善JavaScript程序建议--函数式编程

函数式编程已经在实际应用中经发挥了巨大作用,更有越来越多的语言不断地加入对诸如闭包、匿名函数等的支持,从某种程度上来讲,函数式编程正在逐步同化命令式编程。

建议1:禁用Function构造函数

使用Function构造函数创建的函数具有顶级作用域

var n = 1;
function f(){
    var n = 2;
    var e = new Function("return n;");
    return e;
}
console.log(f()()); // 1

建议2:推荐动态调用函数

使用call和apply方法可以把一个函数转换为方法传递给某个对象。这种行为只是临时的,函数最终并没有作为对象的方法而存在,当函数被调用后,该对象方法会自动被注销。

var a = [1, 5, 3];
var m = Math.max.apply(null, a);
console.log(m); // 5
m.max();    // Uncaught TypeError: m.max is not a function(…)

建议3:使用闭包跨作用域开发

闭包结构的两个特性: (1)封闭性,外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口。 (2)持久性,对于一般函数来说,在调用完毕后,系统自动注销函数,而对于闭包来说,在外部函数调用之后,闭包结构依然保存在系统中,闭包中的数据依然存在,从而实现对数据的持久使用。 示例:使用闭包结构能够跟踪动态环境中数据的实时变化,并即时存储

function f(x){
    var a = x;
    var innerFun = function(){
        return a;
    };
    a++;
    return innerFun;
}
var fn = f(5);
console.log(fn());  // 6

示例:闭包不会因为外部函数环境的注销而消失,会始终存在

<!-- 首先需要执行doFn(),形成三个闭包 -->
<button onclick="doFn();">doFn()</button>
<button onclick="m1();">m1()</button>
<button onclick="m2();">m2()</button>
<button onclick="m3(100);">m3()</button>
var m1, m2, m3;
function doFn(){
    var t = 1;
    m1 = function(){
        console.log(t);
    };
    m2 = function(){
        t++;
    };
    m3 = function(x){
        t = x;
    };
}

建议4:比较函数调用和引用本质

引用函数,多个变量存储的是函数的相同入口指针(地址)。 调用函数,执行该函数把返回“值”传递给变量,而不是函数的入口指针(地址)。 示例:函数引用

function f(){
    var x = 5;
    return x;
}
var f1 = f;
var f2 = f;
console.log(f1 === f2); // true

示例:函数调用

function f(){
    var x = 5;
    return function(){  // 返回存储在不同变量中,它们的地址指针是完全不同的
        return x;
    }
}
var f1 = f();   // f1 = function(){ return x; }
var f2 = f();   // f2 = function(){ return x; }
console.log(f1 === f2); // false

建议5:惰性函数求值

惰性函数模式是一种对函数或请求的处理延迟到真正需要结果时进行的通用概念 示例:常规方式

var t;
function f(){
    t = t ? t : new Date();
    return t;
}
f();

示例:闭包方式

var f = (function(){
    var t;
    return function(){  // 每次调用仍需要求值
        t = t ? t : new Date(); 
        return t;
    };
})();
f();

示例:惰性方式,无需每次都求值

var f = function(){
    var t = new Date();
    f = function(){  // 再次调用无需求值
        return t;   
    };
    return f();
};
f();

建议5:惰性载入函数

惰性载入通常解决兼容性问题。要执行的适当代码在实际调用函数时才执行;除第一次调用外,后续调用无需执行判断分支。

function fn(index){
    switch (index){
        case 1:
            fn = function(){
                console.log("Hello");
            };
            break;
        case 2:
            fn = function(){
                console.log("안녕하세요");
            };
            break;
        default:
            fn = function(){
                console.log("你好");
            }
    }
    return fn();
}
fn(2);  // 안녕하세요

建议6:函数绑定

开发中使用回调函数和处理程序经常会遇到this绑定问题。函数绑定可以提供一个可选的执行上下文传递给函数。 示例:自定义bind

function bind(fn, context){
    return function(){
        return fn.apply(context, arguments);
    }
}
var handler = {
    message: "Hello",
    handlerClick: function(event){
        console.log(this.message);
    }
};
document.getElementById("btn").addEventListener("click", bind(handler.handlerClick, handler));

示例:ES5中bind

var handler = {
    message: "Hello",
    handlerClick: function(event, currentDom){
        console.log(this.message);
        /**
         * 注意使用event.currentTarget代替this,有可能会有问题
         * 触发a和li会有区别!!!
         * <li><a></a></li>
         */
        console.log(event.currentTarget);
        console.log(currentDom === event.currentTarget);
    }
};
// this指向handler,事件处理函数的原this无法传递
document.getElementById("btn").addEventListener("click", handler.handlerClick.bind(handler));
// this指向handler,事件处理函数的原this传递给回调函数
document.getElementById("btn").addEventListener("click", function(event){
    handler.handlerClick(event, this);
});

建议7:函数套用和柯里化

套用指的是将函数与传递给它的参数相结合,产生一个新的函数。 示例:函数套用

// 通过Function扩展函数
Function.prototype.method = function(name, fn){
    if(!this.prototype[name]){
        this.prototype[name] = fn;
        return this;
    }
};
Function.method("curry", function(){
    var slice = Array.prototype.slice,
        fn = this,
        arys = slice.call(arguments);
    return function(){
        return fn.apply(null, arys.concat(slice.call(arguments)));
    };
});
var add = function(){
    var sum = 0;
    for(var i = 0, len = arguments.length; i < len; i++){
        sum += arguments[i];
    }
    return sum;
};
var f = add.curry(2);
console.log(f(3));  // 5

柯里化是利用已有的函数,再创建一个动态的函数,该动态函数内部还是通过已有的函数来发生作用。 示例:柯里化

function curry(fn){
    var slice = Array.prototype.slice,
        args = slice.call(arguments, 1);
    return function(){
        return fn.apply(null, args.concat(slice.call(arguments)));
    };
}
var add = function(){
    var sum = 0;
    for(var i = 0, len = arguments.length; i < len; i++){
        sum += arguments[i];
    }
    return sum;
};
var f = curry(add, 2);
console.log(f(3));  // 5

建议8:重视函数节流

节流函数的设计思想就是让某些代码可以在间断情况下连续重复执行,实现的方法是使用定时器对函数进行节流。对于resize、mousemove、mouseover、mouseout等事件尤为重要!!

function throttle(method, context){
    clearTimeout(method.tId);
    method.tId = setTimeout(function(){
        method.call(context);
    }, 1000);
}
function reszie(){
    console.log(1);
}
window.onresize = function(){
    throttle(reszie);   // 注意,必须是具名函数
};

由于setTimeout性能很差,下述提供一种灵活性方式,但有一定的局限性。

/**
 * 节流函数(简易)
 * @param now 当前毫秒值
 * @param method 方法
 * @param context 上下文
 */
function throttle(now, method, context){
    var time = +new Date();
    throttle = function(now, method, context){
        if(now - time > 1000){
            time = now;  // 更新time
            method.call(context);
        }
    };
    throttle(now, method, context);
}

function reszie(){
    console.log(1);
}
window.onresize = function(){
    throttle(+new Date(), reszie, null);
}

区别于传统节流函数,该简易节流函数调用时立即被执行,且需传递当前毫秒值!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 函数表达式

    定义函数的方式有两种:第一种是“函数声明”,另一种就是“函数表达式”。 “函数声明”会被提升,意味着把函数声明放在调用它的语句后面。 示例1:

    奋飛
  • JavaScript设计模式--单例模式

    保证一个类仅有一个实例,并提供一个访问它的全局访问点。 当单击登陆按钮,页面中出现一个登陆浮窗,这个登陆浮窗是唯一的,无论单击多少次登陆按钮,这个浮窗都只会...

    奋飛
  • JavaScript异步编程设计快速响应的网络应用

    因为setTimeout回调在while循环结束运行之前不可能被触发! 调用setTimeout时,会有一个延时事件排入队列。然后继续执行下一行代码,直到再...

    奋飛
  • 函数表达式

    定义函数的方式有两种:第一种是“函数声明”,另一种就是“函数表达式”。 “函数声明”会被提升,意味着把函数声明放在调用它的语句后面。 示例1:

    奋飛
  • JavaScript模式 读书笔记三

    函数是第一类对象 first-class object,可以作为带有属性和方法的值以及参数进行传递。

    lilugirl
  • js函数、作用域和闭包

    2.1 用function命令声明函数 function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数,函数体放在大括号里面

    bamboo
  • 理解运用JS的闭包、高阶函数、柯里化

    JS的闭包,是一个谈论得比较多的话题了,不过细细想来,有些人还是理不清闭包的概念定义以及相关的特性。

    书童小二
  • js基础知识

    最主要区别是函数名称 在函数表达式中可省略函数声明,从而创建匿名函数

    星辉
  • JavaScript 基础(六) 数组方法 闭包

    在一个对象中绑定函数,称为这个对象的方法。 在JavaScript 中,对象的定义是这样的;     var guagua = {         na...

    用户1197315
  • JavaScript设计模式--单例模式

    保证一个类仅有一个实例,并提供一个访问它的全局访问点。 当单击登陆按钮,页面中出现一个登陆浮窗,这个登陆浮窗是唯一的,无论单击多少次登陆按钮,这个浮窗都只会...

    奋飛

扫码关注云+社区

领取腾讯云代金券