系列更文前三篇文章,围绕了一个重要的知识点:"函数"。
函数调用栈、函数执行上下文、函数做用域到闭包。可见不理解函数式编程,代码都撸很差。javascript
函数与其它数据类型同样,能够做为值赋给变量,做为参数传递或返回值返回,也能够像对象同样给函数建立属性(不推荐给函数加属性,虽然可用)。html
// 函数声明 function getName() { //... } // 函数表达式 var getName = function() { //... } // 匿名函数 setTimeout(function(){ //... }, 1000); // 自执行函数 (function(){ //... })();
函数声明在"执行上下文建立阶段"就会进行声明并赋值,而var
声明变量会初始化为undefined
,实际赋值会等到"执行上下文执行阶段"。函数表达式使用var
来声明,所以它遵循的是变量声明的规则。( 若是函数名与变量重名,函数优先赋值)前端
"函数声明优先级高于变量声明和函数表达式,自称一等公民。"java
// 代码书写: console.log(getName); getName(); var getName; getName = '个人名字'; function getName(){ //... } console.log(getName); // 实际执行 var getName; // 变量名与函数名重名,函数优先赋值 function getName() { //... } console.log(getName); getName(); getName = '个人名字'; console.log(getName);
函数式编程是一种编程思惟方式,它建议咱们在程序编写时,对复用性高的功能代码进行函数封装,实现代码的高复用性。git
新手朋友每每是一块代码屡次出如今不一样的地方,常见的例子就是ajax
请求方法运用,在须要请求后端数据时屡次出现一串ajax
请求代码。程序员
若是想要对ajax
请求统一作异常处理,或管理后端返回状态码,是否是每处代码都要修改???可是若是把ajax
请求代码封装成一个函数,接口url
和数据data
经过参数传递到函数内部处理,后期扩展维护都方便修改,复用性扩展性都更加优秀。github
因此实际敲代码过程当中,要常常提醒本身运用函数式编程的思惟方式,只要有可能出现屡次的业务逻辑代码,那么就要考虑是否封装成函数,以便后续统一调用。ajax
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()
...编程
纯函数:相同的输入对应相同的输出,稳定没有反作用(不改变外部变量的值)
相同的参数传入调用,要有相同的结果输出,概念有点绕,上代码栗子:redux
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.push(); // 数组尾部插入 array.pop(); // 删除并返回数组最后一个元素 array.unshift(); // 数组头部插入 array.shift(); // 删除并返回数组第一元素 array.splice(); // 删除元素,并向数组添加元素 array.reverse(); // 颠倒数组元素的顺序 array.sort(); // 排序数组元素 // 纯函数 array.slice(); // 数组中返回选定的元素 array.concat(); // 链接数组,并发挥新数组 array.join(); // 按分隔符链接数组,返回字符串
流行框架中状态管理就是纯函数的实践应用,引用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函数做为参数
高阶函数的概念很简单,"自己是函数,参数是函数,或返回值是函数"。
参考文档:
系列更文请关注专栏:《前端进击的巨人》,不断更新中。。。
本文首发Github,期待Star!
https://github.com/ZengLingYong/blog
做者:以乐之名 本文原创,有不当的地方欢迎指出。转载请指明出处。