JS高阶编程函数:惰性函数

  • 能执行一次绝不执行第二次

  • 获取元素样式 getComputedStyle ,getComputedStyle在IE 6、7、8下不兼容,不兼容用 currentStyle

    1. function getCss(element, attr) {
    2. if(window.getComputedStyle){
    3. return window.getComputedStyle(element)[attr];
    4. }
    5. //IE6~8
    6. return element.currentStyle[attr];
    7. }
    8. console.log(getCss(document.body,'width'));
    9. console.log(getCss(document.body,'padding'));
    10. console.log(getCss(document.body,'margin'));

上述方法,每一次执都需要做兼容处理:其实这种操作是没必要的,第一次执行已经知道兼容情况了,后期再执行方法(浏览器也没有刷新、也没有换浏览器)兼容校验是没必要处理的

  1. let isCompatible = 'getComputedStyle' in window;
  2. function getCss(element, attr) {
  3. if(isCompatible){
  4. return window.getComputedStyle(element)[attr];
  5. }
  6. //IE6~8
  7. return element.currentStyle[attr];
  8. }
  9. console.log(getCss(document.body,'width'));
  10. console.log(getCss(document.body,'padding'));
  11. console.log(getCss(document.body,'margin'));

上述方法是一种方案:页面一加载就把是否兼容处理了,后期执行方法直接基于变量的值判断使用哪个办法即可。

惰性思想来解决:函数重构

  • getCss是全局函数
  • 第一次执行方法会产生闭包
    1. function getCss(element, attr) {
    2. /*
    3. * EC(1) 闭包
    4. * element = BODY
    5. * attr = 'width'
    6. * 作用域链:<EC(1),EC(G)>
    7. */
    8. if(window.getComputedStyle) {
    9. // 把全局的getCss重构成为具体的小函数
    10. getCss = function(element, attr) {
    11. return window.getComputedStyle(element)[attr];
    12. }
    13. } else {
    14. getCss = function(element, attr) {
    15. return element.currentStyle[attr];
    16. }
    17. }
    18. // 重构后getCss只是被重新赋值了新函数,并未执行,需要执行一次:确保第一次调用的getCss函数可以获取到结果
    19. return getCss(element, attr)
    20. }
    21. console.log(getCss(document.body,'width'));
    22. // 第二次执行:执行是的重构后的小函数,这样告别了兼容校验的操作
    23. console.log(getCss(document.body,'padding'));
    24. console.log(getCss(document.body,'margin'));

JS高阶函数:柯里化函数

  • 预处理思想 (把后面需要的值预先存储起来)
  • 应用的也是闭包的机制

面试题:实现函数fn,让其有如下功能(百度二面)
题目:

  1. let res = fn(1,2)(3);
  2. console.log(res); // =>6
  1. function fn(m,n) {
  2. return function anonymous (c) {
  3. return m + n + c;
  4. }
  5. }
  6. let res = fn(1,2)(3);
  7. console.log(res); // =>6
  1. function fn(...args) {
  2. // ES6中的剩余运算符: ...args,把传递进来的实参获取到(排除基于其他形参获取的信息)
  3. console.log(args); //数组集合
  4. console.log(arguments); //类数组集合
  5. return function anonymous() {
  6. };
  7. };
  8. fn(1,2)

image.png

  1. function fn(...outerArgs) {
  2. // outerArgs存放第一次执行fn传递的实参信息 [1,2]
  3. return function anonymous(...inderArgs) {
  4. // innerArgs存放第二次执行匿名函数传递的实参信息 [3]
  5. // let arr = [...outerArgs, ...innerArgs]
  6. let arr = outerArgs.concat(innerArgs);
  7. // 用reduce实现数组求和
  8. // return arr.reduce((total, item) => total+item)
  9. return arr.reduce(function(total, item) {
  10. return total + item
  11. })
  12. };
  13. }
  14. let res = fn(1,2)(3);
  15. 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:数组中用来迭代遍历每一项的,可以把每一次处理的结果都拿到,在第一次的处理基础上,进行二次处理…直到数组遍历完成

  1. let arr = [10, 20 , 30, 40, 50];
  2. arr.reduce(function(result, item){
  3. console.log(result, item);
  4. })

image.png

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

image.png
function([result],[item]){} 是callback函数,[value]和callback函数是同级别的。
如果传递了[value]值,则第一次执行callback,里面的[result]存储的参数新信息。
[value]是item迭代的是数组中的第一项,result的初始值

  1. arr.reduce(function([result],[item]){
  2. // ...
  3. }[,value]);
  4. // 传初始值
  5. arr.reduce(function (result, item) {
  6. console.log(result, item); // result=0 item=10(数组第一项)
  7. }, 0);
  8. // 不传初始值
  9. arr.reduce(function (result, item) {
  10. console.log(result, item); // result=10 item=20(数组第一项/第二项)
  11. });
  1. let arr = [10, 20 , 30, 40, 50];
  2. arr.reduce(function(result, item){
  3. console.log(result)
  4. return result+item
  5. }, 0)

image.png

手写reduce()

  1. function reduce(arr, callback, init) {
  2. // 进来后先把源数组克隆一份(浅克隆),再赋值给私有变量arr,后面玩的都是克隆过的,对源数组没影响
  3. arr = arr.slice(0);
  4. // 校验init是否传递
  5. if(typeof init === "undefined") {
  6. init = arr[0];
  7. arr = arr.slice(1);
  8. }
  9. // 依次循环数组的每一项
  10. for(let i =0; i< arr.length; i++) {
  11. let item = arr[i],
  12. index = i;
  13. init = callback(init, item, index);
  14. }
  15. return init
  16. }
  17. let arr = [10, 20, 30, 40, 50];
  18. let result = reduce(arr, function(result, item, index){
  19. return result + item
  20. })
  21. console.log(result)
  22. let result = reduce(arr, function(result, item, index){
  23. return result + item
  24. }, 100)
  25. console.log(result)

需要注意的是,原生reduce,如果没有赋值初始值,index是从1开始的,如果赋值了,index是从0 开始的。
image.png
优化刚才的手写reduce

  1. function reduce(arr, callback, init) {
  2. // 进来后先把源数组克隆一份(浅克隆),再赋值给私有变量arr,后面玩的都是克隆过的,对源数组没影响
  3. arr = arr.slice(0);
  4. // 依次循环数组的每一项
  5. for(let i = 0; i< arr.length; i++) {
  6. if(init === undefined) {
  7. // init没有传递值
  8. init = arr[0];
  9. let item = arr[1],
  10. index=1;
  11. init = callback(init, item, index);
  12. i++;
  13. continue;
  14. }
  15. let item = arr[i],
  16. index = i;
  17. init = callback(init, item, index);
  18. }
  19. return init
  20. }
  21. let arr = [10, 20, 30, 40, 50];
  22. let result = reduce(arr, function(result, item, index){
  23. return result + item
  24. })
  25. console.log(result)
  26. let result = reduce(arr, function(result, item, index){
  27. return result + item
  28. }, 100)
  29. console.log(result)

缺陷: 只要其中某一个init 的结果未返回,为undefined,就会进入 if(init === undefined) 条件中去。

  1. function reduce(arr, callback, init) {
  2. // 进来后先把源数组克隆一份(浅克隆),再赋值给私有变量arr,后面玩的都是克隆过的,对源数组没影响
  3. arr = arr.slice(0);
  4. let result = init;
  5. // 依次循环数组的每一项
  6. for(let i = 0; i< arr.length; i++) {
  7. if(init === undefined && i===0) {
  8. // init没有传递值
  9. result = arr[0];
  10. let item = arr[1],
  11. index=1;
  12. // Arr只有一个元素的时候,不会执行callback,直接返回该元素
  13. if(!item) return result;
  14. result = callback(result, item, index);
  15. i++;
  16. continue;
  17. }
  18. let item = arr[i],
  19. index = i;
  20. result = callback(result, item, index);
  21. }
  22. return result;
  23. }
  24. let arr = [10, 20, 30, 40, 50];
  25. let result = reduce(arr, function(result, item, index){
  26. return result + item
  27. })
  28. console.log(result)
  29. let result = reduce(arr, function(result, item, index){
  30. return result + item
  31. }, 100)
  32. console.log(result)

网上写法:https://segmentfault.com/a/1190000022360279