前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >编写高质量代码:改善JavaScript程序建议--函数式编程

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

作者头像
奋飛
发布2019-08-15 09:47:45
3740
发布2019-08-15 09:47:45
举报
文章被收录于专栏:Super 前端Super 前端

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

建议1:禁用Function构造函数

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

代码语言:javascript
复制
var n = 1;
function f(){
    var n = 2;
    var e = new Function("return n;");
    return e;
}
console.log(f()()); // 1

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

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

代码语言:javascript
复制
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)持久性,对于一般函数来说,在调用完毕后,系统自动注销函数,而对于闭包来说,在外部函数调用之后,闭包结构依然保存在系统中,闭包中的数据依然存在,从而实现对数据的持久使用。 示例:使用闭包结构能够跟踪动态环境中数据的实时变化,并即时存储

代码语言:javascript
复制
function f(x){
    var a = x;
    var innerFun = function(){
        return a;
    };
    a++;
    return innerFun;
}
var fn = f(5);
console.log(fn());  // 6

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

代码语言:javascript
复制
<!-- 首先需要执行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:比较函数调用和引用本质

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

代码语言:javascript
复制
function f(){
    var x = 5;
    return x;
}
var f1 = f;
var f2 = f;
console.log(f1 === f2); // true

示例:函数调用

代码语言:javascript
复制
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:惰性函数求值

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

代码语言:javascript
复制
var t;
function f(){
    t = t ? t : new Date();
    return t;
}
f();

示例:闭包方式

代码语言:javascript
复制
var f = (function(){
    var t;
    return function(){  // 每次调用仍需要求值
        t = t ? t : new Date(); 
        return t;
    };
})();
f();

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

代码语言:javascript
复制
var f = function(){
    var t = new Date();
    f = function(){  // 再次调用无需求值
        return t;   
    };
    return f();
};
f();

建议5:惰性载入函数

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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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:函数套用和柯里化

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

代码语言:javascript
复制
// 通过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

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

代码语言:javascript
复制
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等事件尤为重要!!

代码语言:javascript
复制
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性能很差,下述提供一种灵活性方式,但有一定的局限性。

代码语言:javascript
复制
/**
 * 节流函数(简易)
 * @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);
}

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年10月16日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 建议1:禁用Function构造函数
  • 建议2:推荐动态调用函数
  • 建议3:使用闭包跨作用域开发
  • 建议4:比较函数调用和引用本质
  • 建议5:惰性函数求值
  • 建议5:惰性载入函数
  • 建议6:函数绑定
  • 建议7:函数套用和柯里化
  • 建议8:重视函数节流
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档