资讯专栏INFORMATION COLUMN

前端进击的巨人(四):略知函数式编程

omgdog / 2262人阅读

摘要:自执行函数闭包实现模块化以乐之名程序员产品经理对作用域,以及闭包知识还没掌握的小伙伴,可回阅前端进击的巨人三从作用域走进闭包。参考文档利用闭包实现模块化翻译浅谈中的高阶函数系列更文请关注专栏前端进击的巨人,不断更新中。。。

系列更文前三篇文章,围绕了一个重要的知识点:"函数"
函数调用栈、函数执行上下文、函数作用域到闭包。可见不理解函数式编程,代码都撸不好。

 函数是一等公民

函数与其它数据类型一样,可以作为值赋给变量,作为参数传递或返回值返回,也可以像对象一样给函数创建属性(不推荐给函数加属性,虽然可用)。

函数在实际开发中应用:

函数声明

函数表达式

匿名函数

自执行函数

// 函数声明
function getName() {
    //...    
}

// 函数表达式
var getName = function() {
    //...
}

// 匿名函数
setTimeout(function(){
    //...
}, 1000);

// 自执行函数
(function(){
    //...
})();
何为一等:优先级

函数声明在"执行上下文创建阶段"就会进行声明并赋值,而var声明变量会初始化为undefined,实际赋值会等到"执行上下文执行阶段"。函数表达式使用var来声明,因此它遵循的是变量声明的规则。( 如果函数名与变量重名,函数优先赋值)

"函数声明优先级高于变量声明和函数表达式,自称一等公民。"

// 代码书写:
console.log(getName);
getName();
var getName;
getName = "我的名字";
function getName(){
    //...
}
console.log(getName);

// 实际执行
var getName;            // 变量名与函数名重名,函数优先赋值
function getName() {
    //...
}
console.log(getName);
getName();
getName = "我的名字";
console.log(getName);
函数式编程

函数式编程是一种编程思维方式,它建议我们在程序编写时,对复用性高的功能代码进行函数封装,实现代码的高复用性。

新手朋友往往是一块代码多次出现在不同的地方,常见的例子就是ajax请求方法运用,在需要请求后端数据时多次出现一串ajax请求代码。

如果想要对ajax请求统一做异常处理,或管理后端返回状态码,是不是每处代码都要修改???但是如果把ajax请求代码封装成一个函数,接口url和数据data通过参数传递到函数内部处理,后期扩展维护都方便修改,复用性扩展性都更加优秀。

所以实际敲代码过程中,要经常提醒自己运用函数式编程的思维方式,只要有可能出现多次的业务逻辑代码,那么就要考虑是否封装成函数,以便后续统一调用。

function sumScore(list) {
    var totalScore = 0
    for (var i = 0; i < list.length; i++) {
        totalScore += list[i];        
    }
    return totalScore;    
}

var list = [10, 8, 9, 7];
var totalScore = sumScore(list);    // 计算总分

TIPS: 函数名建议使用动词,如addUser(),sumScore(),getUser()...

纯函数
纯函数:相同的输入对应相同的输出,稳定没有副作用(不改变外部变量的值)
相同的输入,相同的输出

相同的参数传入调用,要有相同的结果输出,概念有点绕,上代码栗子:

function getDate() {
    return new Date();
}
var dateOne = getDate();
var dateTwo = getDate();
var dateThr = getDate();

上述代码中调用了三次getDate(),三次返回的值都不一样。相同的输入并没有相同的输出,所以getDate()并不是一个纯函数。

TIPS:函数中使用new Date(),Math.random(), 异步等都可能造成函数不稳定。

没有副作用(不改变外部环境的值)

部分小伙伴的代码,在函数里面直接修改参数的值,这是一种非常不推荐的做法,这样做会造成代码环境不可控制,污染外部变量环境,一旦出现错误排查起来:心累,三个字心好累。

函数有自己的局部作用域,因此函数中,对需要使用到的变量,管控在自身的作用域下。如果需要修改外部参数的值,通过函数返回值返回给函数调用者。修改外部参数值的操作不在函数内进行,确保对外部环境没有副作用。

TIPS:参数为引用类型时,参数复制的是地址指针,避免修改了引用类型中属性值污染外部环境,如需使用建议手动深拷贝赋值。

function getGirlGift(list) {
    // 避免污染参数为引用类型的list,对list深拷贝
    var newList = JSON.parse(JSON.stringify(list));
    newList.map(girl => {
        girl.gift = girl.age > 18 ? "lipstick" : "chocolates";
    });
    return newList;    // 返回新值
}

var girlList = [
    {name: "Kelly", age: 20},
    {name: "Alic", age: 16},
    {name: "Moon", age: 23},
    {name: "Nana", age: 17}
];

var girlGiftList = getGirlGift(girlList);
girlList         // 原用girlList不变
girlGiftList     // 每个girl多了gift属性
Array对象的函数(纯与不纯)
// 不纯的函数
array.push();       // 数组尾部插入
array.pop();        // 删除并返回数组最后一个元素
array.unshift();    // 数组头部插入
array.shift();      // 删除并返回数组第一元素
array.splice();     // 删除元素,并向数组添加元素
array.reverse();    // 颠倒数组元素的顺序
array.sort();       // 排序数组元素

// 纯函数
array.slice();      // 数组中返回选定的元素
array.concat();     // 连接数组,并发挥新数组
array.join();       // 按分隔符连接数组,返回字符串

>>更多Array对象方法,参考W3C

纯函数的应用:状态管理Redux,Vuex

流行框架中状态管理就是纯函数的实践应用,引用redux的应用,reducer中返回新的状态数据state,但不能去直接去修改state数据,以下为redux中reducer的例子代码:

export default (state = defaultState, action) => {
    let newState = JSON.parse(JSON.stringify(state));

    switch (action.type) {
        case DELETE_TODO_ITEM:
            newState.list.splice(action.value, 1);
        break;
        case ADD_TODO_ITEM:
            if (newState.inputValue.trim().length) {
                newState.list.push(newState.inputValue);
            }
            newState.inputValue = "";
        break;
        case INIT_LIST_ACTION: 
            newState = action.data
        break;
        default: 
        break;
    }

    return newState;
}
"自执行函数 + 闭包" 实现模块化 模块化包括:

私有变量

私有方法

公有变量

公有方法

上篇中《前端进击的巨人(三):从作用域走进闭包》我们讲解了作用域、闭包的原理机制。

"自执行函数可实现块级作用域,而闭包则可实现外部环境对函数作用域内部数据的访问。"

// 自执行函数 + 闭包实现模块化
(function MakeModule(window) {
    var name = "以乐之名";
    var age = 28;
    var job = "程序员";
    
    function changeJob(newJob) {
        job = newJob;
    }
    
    function getName() {
        return name;        
    }
    
   window.modulePublic = {
        changeJob: changeJob,
        getName: getName
    }
})(window);

window.modulePublic.getName();
window.modulePublic.changeJob("产品经理");

对作用域,以及闭包知识还没掌握的小伙伴,可回阅《前端进击的巨人(三):从作用域走进闭包》。

高阶函数
高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回

JavaScript中常用的高阶函数:

Array.prototype.map (映射遍历)

Array.prototype.filter (过滤)

Array.prototype.reducer(累计)

除了内置的高阶函数,我们实际开发中,高阶函数应用的最多就是回调函数了。

function getOrder(url, datas, callBack) {
    return $.post(url, datas, callBack(orderInfo));
}

// getOrder就是一个高阶函数,接收callBack函数作为参数

高阶函数的概念很简单,"本身是函数,参数是函数,或返回值是函数"

参考文档:

JavaScript利用闭包实现模块化

【翻译】 - 浅谈JavaScript中的高阶函数

系列更文请关注专栏:《前端进击的巨人》,不断更新中。。。

本文首发Github,期待Star!
https://github.com/ZengLingYong/blog

作者:以乐之名
本文原创,有不当的地方欢迎指出。转载请指明出处。

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

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

相关文章

  • 前端进击巨人(六):知否知否,须知this

    摘要:有关函数柯里化的详解,请回阅前端进击的巨人五学会函数柯里化。构造函数中的通过操作符可以实现对函数的构造调用。在了解构造函数中的前,有必要先了解下实例化对象的过程。 showImg(https://segmentfault.com/img/bVburMp?w=800&h=600); 常见this的误解 指向函数自身(源于this英文意思的误解) 指向函数的词法作用域(部分情况) th...

    Andrman 评论0 收藏0
  • 前端进击巨人(一):执行上下文与执行栈,变量对象

    摘要:在中,通过栈的存取方式来管理执行上下文,我们可称其为执行栈,或函数调用栈。而处于栈顶的是当前正在执行函数的执行上下文,当函数调用完成后,它就会从栈顶被推出理想的情况下,闭包会阻止该操作,闭包后续文章深入详解。 写在开篇 已经不敢自称前端小白,曾经吹过的牛逼总要一点点去实现。 正如前领导说的,自己喝酒吹过的牛皮,跪着都得含着泪去实现。 那么没有年终完美总结,来个新年莽撞开始可好。 进击巨...

    _Suqin 评论0 收藏0
  • 前端进击巨人(七):走进面向对象,原型与原型链,继承方

    摘要:除了以上介绍的几种对象创建方式,此外还有寄生构造函数模式稳妥构造函数模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向对象 是以 对象 为中心的编程思想,它的思维方式是构造。 面向对象 编程的三大特点:封装、继承、多态: 封装:属性方法的抽象 继承:一个类继承(复制)另一个类的属性/方法 多态:方...

    wums 评论0 收藏0
  • 前端进击巨人(五):学会函数柯里化(curry)

    摘要:函数柯里化是把支持多个参数的函数变成接收单一参数的函数,并返回一个函数能接收处理剩余参数,而反柯里化就是把参数全部释放出来。但在一些复杂的业务逻辑封装中,函数柯里化能够为我们提供更好的应对方案,让我们的函数更具自由度和灵活性。 showImg(https://segmentfault.com/img/bVburN1?w=800&h=600); 柯里化(Curring, 以逻辑学家Has...

    chengtao1633 评论0 收藏0
  • 前端进击巨人(二):栈、堆、队列、内存空间

    摘要:中有三种数据结构栈堆队列。前端进击的巨人一执行上下文与执行栈,变量对象中解释执行栈时,举了一个乒乓球盒子的例子,来演示栈的存取方式,这里再举个栗子搭积木。对于基本类型,栈中存储的就是它自身的值,所以新内存空间存储的也是一个值。 面试经常遇到的深浅拷贝,事件轮询,函数调用栈,闭包等容易出错的题目,究其原因,都是跟JavaScript基础知识不牢固有关,下层地基没打好,上层就是豆腐渣工程,...

    edgardeng 评论0 收藏0

发表评论

0条评论

omgdog

|高级讲师

TA的文章

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