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

栏目: 编程语言 · 发布时间: 5年前

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

系列更文前三篇文章,围绕了一个重要的知识点: "函数"

函数调用栈、函数执行上下文、函数作用域到闭包。可见不理解函数式编程,代码都撸不好。

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

函数是一等公民

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

函数在实际开发中应用:

  1. 函数声明
  2. 函数表达式
  3. 匿名函数
  4. 自执行函数
// 函数声明
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;
}

"自执行函数 + 闭包" 实现模块化

模块化包括:

  1. 私有变量
  2. 私有方法
  3. 公有变量
  4. 公有方法

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

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

// 自执行函数 + 闭包实现模块化
(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函数作为参数

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

参考文档:

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

本文首发Github,期待Star!

https://github.com/ZengLingYong/blog

作者:以乐之名


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Designing Data-Intensive Applications

Designing Data-Intensive Applications

Martin Kleppmann / O'Reilly Media / 2017-4-2 / USD 44.99

Data is at the center of many challenges in system design today. Difficult issues need to be figured out, such as scalability, consistency, reliability, efficiency, and maintainability. In addition, w......一起来看看 《Designing Data-Intensive Applications》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

html转js在线工具
html转js在线工具

html转js在线工具