JS高阶编程函数:惰性函数
- 懒
能执行一次绝不执行第二次
获取元素样式
getComputedStyle
,getComputedStyle
在IE 6、7、8下不兼容,不兼容用currentStyle
。function getCss(element, attr) {
if(window.getComputedStyle){
return window.getComputedStyle(element)[attr];
}
//IE6~8
return 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~8
return 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)