JS高阶编程函数:惰性函数
- 懒
能执行一次绝不执行第二次
获取元素样式
getComputedStyle,getComputedStyle在IE 6、7、8下不兼容,不兼容用currentStyle。function getCss(element, attr) {if(window.getComputedStyle){return window.getComputedStyle(element)[attr];}//IE6~8return element.currentStyle[attr];}console.log(getCss(document.body,'width'));console.log(getCss(document.body,'padding'));console.log(getCss(document.body,'margin'));
上述方法,每一次执都需要做兼容处理:其实这种操作是没必要的,第一次执行已经知道兼容情况了,后期再执行方法(浏览器也没有刷新、也没有换浏览器)兼容校验是没必要处理的
let isCompatible = 'getComputedStyle' in window;function getCss(element, attr) {if(isCompatible){return window.getComputedStyle(element)[attr];}//IE6~8return element.currentStyle[attr];}console.log(getCss(document.body,'width'));console.log(getCss(document.body,'padding'));console.log(getCss(document.body,'margin'));
上述方法是一种方案:页面一加载就把是否兼容处理了,后期执行方法直接基于变量的值判断使用哪个办法即可。
惰性思想来解决:函数重构
- getCss是全局函数
- 第一次执行方法会产生闭包
function getCss(element, attr) {/** EC(1) 闭包* element = BODY* attr = 'width'* 作用域链:<EC(1),EC(G)>*/if(window.getComputedStyle) {// 把全局的getCss重构成为具体的小函数getCss = function(element, attr) {return window.getComputedStyle(element)[attr];}} else {getCss = function(element, attr) {return element.currentStyle[attr];}}// 重构后getCss只是被重新赋值了新函数,并未执行,需要执行一次:确保第一次调用的getCss函数可以获取到结果return getCss(element, attr)}console.log(getCss(document.body,'width'));// 第二次执行:执行是的重构后的小函数,这样告别了兼容校验的操作console.log(getCss(document.body,'padding'));console.log(getCss(document.body,'margin'));
JS高阶函数:柯里化函数
- 预处理思想 (把后面需要的值预先存储起来)
- 应用的也是闭包的机制
面试题:实现函数fn,让其有如下功能(百度二面)
题目:
let res = fn(1,2)(3);console.log(res); // =>6
function fn(m,n) {return function anonymous (c) {return m + n + c;}}let res = fn(1,2)(3);console.log(res); // =>6
function fn(...args) {// ES6中的剩余运算符: ...args,把传递进来的实参获取到(排除基于其他形参获取的信息)console.log(args); //数组集合console.log(arguments); //类数组集合return function anonymous() {};};fn(1,2)

function fn(...outerArgs) {// outerArgs存放第一次执行fn传递的实参信息 [1,2]return function anonymous(...inderArgs) {// innerArgs存放第二次执行匿名函数传递的实参信息 [3]// let arr = [...outerArgs, ...innerArgs]let arr = outerArgs.concat(innerArgs);// 用reduce实现数组求和// return arr.reduce((total, item) => total+item)return arr.reduce(function(total, item) {return total + item})};}let res = fn(1,2)(3);console.log(res)
解析闭包的应用:大函数fn执行 fn(1,2) 返回了一个小函数anonymous,准确的来说返回的是小函数anonymous形成的堆内存地址 ox000000 ,地址紧接着再执行一次 fn(1,2)(3) ,此时fn形成的私有上下文不会被释放,比如 let f= fn(1,2) 也不会被释放,被f占用了,这种是不会被释放。 fn(1,2)(3) 是临时不释放,这时outerArgs的[1,2]就被保存下来(预先存储)。
柯里化函数:第一次执行大函数,形成一个闭包(原因:返回一个小函数,大函数里面的一些东西被外面占用,不被释放),把一些信息存储到闭包中(传递的实参信息或当前闭包中声明的一些私有变量等信息);等到后面需要把返回的小函数anonymous执行,遇到一些非自己私有变量,则向其上级上下文中中查找(也就是把之前存储在闭包中的信息获取到);
Array.prototype.reduce()
函数接收4个参数:
- Accumulator (acc) (累计器)
- Current Value (cur) (当前值)
- Current Index (idx) (当前索引)
- Source Array (src) (源数组)
reduce:数组中用来迭代遍历每一项的,可以把每一次处理的结果都拿到,在第一次的处理基础上,进行二次处理…直到数组遍历完成
let arr = [10, 20 , 30, 40, 50];arr.reduce(function(result, item){console.log(result, item);})

let arr = [10, 20 , 30, 40, 50];arr.reduce(function(result, item){console.log(result, item)return result+item})

function([result],[item]){} 是callback函数,[value]和callback函数是同级别的。
如果传递了[value]值,则第一次执行callback,里面的[result]存储的参数新信息。
[value]是item迭代的是数组中的第一项,result的初始值
arr.reduce(function([result],[item]){// ...}[,value]);// 传初始值arr.reduce(function (result, item) {console.log(result, item); // result=0 item=10(数组第一项)}, 0);// 不传初始值arr.reduce(function (result, item) {console.log(result, item); // result=10 item=20(数组第一项/第二项)});
let arr = [10, 20 , 30, 40, 50];arr.reduce(function(result, item){console.log(result)return result+item}, 0)
手写reduce()
function reduce(arr, callback, init) {// 进来后先把源数组克隆一份(浅克隆),再赋值给私有变量arr,后面玩的都是克隆过的,对源数组没影响arr = arr.slice(0);// 校验init是否传递if(typeof init === "undefined") {init = arr[0];arr = arr.slice(1);}// 依次循环数组的每一项for(let i =0; i< arr.length; i++) {let item = arr[i],index = i;init = callback(init, item, index);}return init}let arr = [10, 20, 30, 40, 50];let result = reduce(arr, function(result, item, index){return result + item})console.log(result)let result = reduce(arr, function(result, item, index){return result + item}, 100)console.log(result)
需要注意的是,原生reduce,如果没有赋值初始值,index是从1开始的,如果赋值了,index是从0 开始的。
优化刚才的手写reduce
function reduce(arr, callback, init) {// 进来后先把源数组克隆一份(浅克隆),再赋值给私有变量arr,后面玩的都是克隆过的,对源数组没影响arr = arr.slice(0);// 依次循环数组的每一项for(let i = 0; i< arr.length; i++) {if(init === undefined) {// init没有传递值init = arr[0];let item = arr[1],index=1;init = callback(init, item, index);i++;continue;}let item = arr[i],index = i;init = callback(init, item, index);}return init}let arr = [10, 20, 30, 40, 50];let result = reduce(arr, function(result, item, index){return result + item})console.log(result)let result = reduce(arr, function(result, item, index){return result + item}, 100)console.log(result)
缺陷: 只要其中某一个init 的结果未返回,为undefined,就会进入 if(init === undefined) 条件中去。
function reduce(arr, callback, init) {// 进来后先把源数组克隆一份(浅克隆),再赋值给私有变量arr,后面玩的都是克隆过的,对源数组没影响arr = arr.slice(0);let result = init;// 依次循环数组的每一项for(let i = 0; i< arr.length; i++) {if(init === undefined && i===0) {// init没有传递值result = arr[0];let item = arr[1],index=1;// Arr只有一个元素的时候,不会执行callback,直接返回该元素if(!item) return result;result = callback(result, item, index);i++;continue;}let item = arr[i],index = i;result = callback(result, item, index);}return result;}let arr = [10, 20, 30, 40, 50];let result = reduce(arr, function(result, item, index){return result + item})console.log(result)let result = reduce(arr, function(result, item, index){return result + item}, 100)console.log(result)
