Ashun's 技術駅 Ashun's 技術駅
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • Vue
  • 现代web布局
  • React
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 技术资源
  • 第一阶段

    • HTML
  • 第二阶段

    • JavaScript
  • 第三阶段

    • Vue
  • 第四阶段

    • 实战项目
  • 每周测试

    • 每周
  • 其他

    • Vue引入UI框架
    • Web前端面试
    • Vue3-resource
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 福利资源
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Ashun

前端界的小学生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • Vue
  • 现代web布局
  • React
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 技术资源
  • 第一阶段

    • HTML
  • 第二阶段

    • JavaScript
  • 第三阶段

    • Vue
  • 第四阶段

    • 实战项目
  • 每周测试

    • 每周
  • 其他

    • Vue引入UI框架
    • Web前端面试
    • Vue3-resource
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 福利资源
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 01变量
  • 02常量
  • 03js数据类型
  • 04运算、判断、条件
  • 05数字类型
  • 06字符串
  • 07布尔值
  • 08空值
  • 09未定义
  • 10唯一值
  • 11对象
  • 13数组
  • 14函数
  • 15日期对象
  • 16正则表达式
  • 17异常错误对象
  • 18BOM 浏览器对象模型
  • 19DOM文档对象模型
  • 20json
  • 21Ajax
  • 22jQuery
  • 23函数进阶
    • 一、变量提升给函数带来的问题
    • 二、修改 this 指针
      • call、apply、bind
    • 三、高阶函数
    • 四、函数柯里化
      • 1、创建函数柯里化
      • 2、参数递归
    • 五、节流函数和防抖函数
      • 1、节流函数
      • 2、防抖函数
      • 3、优化版的防抖函数
  • 24面向对象
  • 《javascript(es5)》
ashun
2022-02-14
目录

23函数进阶

# 函数进阶

# 一、变量提升给函数带来的问题

//报错,fn不是一个函数,
//原因是var变量提升在全局作用域添加了一个fn为undefined,
//所以undefined无法执行
fn();
var fn = function () {
  console.log(123);
};
1
2
3
4
5
6
7

# 二、修改 this 指针

//把函数fn的指针修改为指向对象obj
function fn(num) {
  console.log(this);
  console.log(num);
}
let obj = {
  name: "小明",
};
fn.call(obj, 123);
1
2
3
4
5
6
7
8
9

# call、apply、bind

[warning]call\apply\bind有什么区别?{% em type="red" %} 面试题 {% endem %}

1、call和apply是返回值,bind是返回函数

2、call和bind是选项式参数、apply是数组参数

var obj = {
  name:"小明"
}
function fn(a,b,c){
  console.log(this)
  console.log(a)
  console.log(b)
  console.log(c)
}

// fn.call(obj,1,2,3)
// fn.apply(obj,[1,2,3])
var f = fn.bind(obj,1,2,3)
f()
//把对象obj里面的fn2函数的指针修改为指向函数fn的this
function fn(){
  obj.fn2.call(this)
}

let obj = {
  fn2:function(){
    console.log(this)
  }
}
fn()


//案例
var name = "小明"
var obj = {
  name:"小红",
  obj2:{
    name:"小绿",
    fn:function(){
      var a = function(){//虽然函数外层是obj2的作用域,但是因为函数默认指向了window,所以这里指的是window
        console.log(this.name)//小明
      }
      a()
    }
  }
}
obj.obj2.fn()

var name = "小明"
var obj = {
  name:"小红",
  obj2:{
    name:"小绿",
    fn:function(){
      var a = ()=>{//箭头函数让this绑定在当前作用域下,无法修改this指针
        console.log(this.name)//小绿
      }
      a()
    }
  }
}
obj.obj2.fn()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

[!cogs]

%accordion% 模块标题 %accordion%

<script>
    var obj = {
        name: "娜美"
    }
    function fn(a, b, c) {
        console.log(this);
        console.log(a);
        console.log(b);
        console.log(c);
    }
    fn.call(obj, 1, 2, 3) //返回值——是选择式参数
    // fn.apply(obj,[1,2,3])//返回值——是选择式参数
    // var f=fn.bind(obj,1,2,3)//返回函数——是数组参数
    // f()

</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

%/accordion%

%accordion% 模块标题 %accordion%

<script>
    var obj = {
        name: "娜美",
        fn1: function () {
            fn2.call(this) //修改他指针
        },
    }

    function fn2() {
        console.log(this);
    }
    obj.fn1()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

%/accordion%

%accordion% 练习 %accordion%

    
<script>
    var name = "娜美"
    var obj = {
        name: "妮可罗宾",
        obj2: {
            name: "大和",
            fn: function () {
                var a = function () {
                    // console.log(this);
                    console.log(this.name);//娜美
                }
                a()
            },
        },
    }
    obj.obj2.fn()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

%/accordion%

%accordion% 函数tkis %accordion%

<script>
    var name = "娜美"
    var obj = {
        name: "妮可罗宾",
        obj2: {
            name: "大和",
            fn: function () {

                var a = () => {
                    console.log(this.name); //大和
                }

                a()
            },
        },
    }
    obj.obj2.fn()
</script>


<script>
    var name = "娜美"
    var obj = {
        name: "妮可罗宾",
        obj2: {
            name: "大和",
            fn: function () {
                var a = () => {
                    console.log(this.name); //大和
                }
                a.call() //就该这里
            },
        },
    }
    obj.obj2.fn()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

%/accordion%

# 三、高阶函数

[info] 高阶函数

就是使函数抽象化,抽象就是隐藏更具体的实现细节,从更高的层次看待我们要解决的问题

// 给所有的数组添加一个自定义迭代方法
let arr = [1, 2, 3, 4, 5];
// arr.forEach(function(item,i){
//   console.log(item)
// })

Array.prototype.myForEach = function (f) {
  let arr = this;
  for (let i = 0; i < arr.length; i++) {
    f(arr[i], i);
  }
};

arr.myForEach(function (item, i) {
  console.log(i);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

[!cogs]

%accordion% 高阶函数 %accordion%

[warning] 函数myForEach

给所有的数组都添加一个myForEach的方法

<script>
    Array.prototype.myForEach = function (f) {
        var arr = this
        for (var i = 0; i < arr.length; i++) {
            f(arr[i], i)
        }
    }
    var arr = [1, 2, 3, 4, 5]
    arr.myForEach(function (item, idx) {//idx索引是从零开始
        // console.log(item);
        console.log(idx);//0,1,2,3,4
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

%/accordion%

# 四、函数柯里化

[info] 柯里化

函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数。

目的是复用参数。

# 1、创建函数柯里化

function fn(a, b) {
  return a + b;
}
fn(1, 2);
fn(1, 3);

//复用参数
function fn(a) {
  return function (b) {
    console.log(a + b);
  };
}

let f = fn(1);
f(2);
f(3);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2、参数递归

//参数固定
function curry(f, args = []) {
  //柯里化函数
  return function () {
    let arr = [...args, ...arguments];

    if (arr.length < f.length) {
      //如果add(1)(2)传的数量小于argSum的形参数量
      return curry.call(this, f, arr); //就递归
    } else {
      return f.apply(this, arr); //如果数量相等,就返回累加的数组
    }
  };
}

function argSum(a, b, c, d) {
  //累加实际参数,并且提供一个数值给柯里化函数进行比较
  let arr = Array.from(arguments); //获取返回来的实际参数

  let sum = arr.reduce(function (a, b) {
    //累加
    return a + b;
  });
  return sum;
}

let add = curry(argSum);
console.log(add(1)(2)(3)(4)); //10
console.log(add(1, 2)(3, 4)); //10
console.log(add(1, 2)(3)(4)); //10
console.log(add(1)(2)(3, 4)); //10

//参数不固定
function curry(f) {
  //柯里化函数
  let args = [];
  return function cb(...arg) {
    if (arg.length) {
      args = [...args, ...arg];
      return cb;
    } else {
      return f.apply(this, args);
    }
  };
}

function addArg(...arr) {
  //累加
  let sum = arr.reduce(function (a, b) {
    return a + b;
  });
  return sum;
}

let add = curry(addArg);
console.log(add(1)(2)(3)(4)()); //10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

[!cogs]

%accordion% 函数柯里化🚀 %accordion%

{% em type="red" %} 复用参数 {% endem %}

<script>
    function fn(a) {
        return function (b) {
            return a + b
        }
    }
    var f = fn(1)//固定值
    console.log(f(2));//3
    console.log(f(3));//4
</script>
1
2
3
4
5
6
7
8
9
10

%/accordion%

%accordion% 参数固定 %accordion%

<script>
    function curry(fn,arr=[]){
        return function(){
            var arg=[...arguments]
            var len=fn.length
            var allArr=[...arg,...arr]

            if(allArr.length<len){
                return curry(fn,allArr)
            }else{
                return fn(...allArr)
            }
        }
    }

    var handler=curry(function(a,b,c,d){
        var arr=[...arguments]
        return arr.reduce(function(x,y){
            return x+y
        })
    })
    console.log(handler(1)(2,3)(4));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

%/accordion%

%accordion% 参数不固定 %accordion%

<script>
    function curry(fn, arg = []) {
        return function () {
            var allArr = [...arg, ...arguments];

            if (arguments.length) {
                return curry(fn, allArr);
            } else {
                return fn(...allArr);
            }
        };
    }
    var handler = curry(function () {
        var arr = [...arguments];
        return arr.reduce(function (x, y) {
            return x + y;
        });
    });
    console.log(handler(1)(2)(3)(4));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

%/accordion%

# 五、节流函数和防抖函数

# 1、节流函数

[info] 节流函数

在某段时间内,不管触发了多少次回调,只认第一次触发,无视后面次数产生的触发,并在计时结束时给予响应。

var div = document.querySelector("div");

function throttle(fn, time) {
  //节流函数
  var last = 0;
  return function () {
    var now = new Date().getTime();

    if (now - last >= time) {
      last = now;
      fn.apply(this, arguments);
    }
  };
}
var clickHandler = throttle(function () {
  //具体逻辑
  console.log("触发了");
}, 3000);

div.onclick = clickHandler;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

[!cogs]

%accordion% 节点函数🚀 %accordion%

{% em type="red" %} 第一次触发,就执行,如再继续触发下去就等待3秒钟才执行。 {% endem %}

<button class="btn">按钮</button>
<script>
    var btn = document.querySelector(".btn");
    function throttle(fn, delayTime) {
        var endTime = 0;
        return function () {
            var nowTime = +new Date();
            if (nowTime - endTime > delayTime) {
                fn();
                endTime = nowTime;
            }
        };
    }
    var handler = throttle(function () {
        console.log(123);
    }, 3000);

    btn.addEventListener("click", handler);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

%/accordion%

# 2、防抖函数

[info] 防抖函数

在某段时间内,每次触发都会重新计时,直到最后一次触发超过了限定的时间。

var div = document.querySelector("div");

function debounce(fn, time) {
  var timer;
  return function () {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(function () {
      fn.apply(this, arguments);
    }, time);
  };
}
var clickHandler = debounce(function () {
  console.log("触发了");
}, 3000);

div.onclick = clickHandler;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

%accordion% 防抖函数🚀 %accordion%

{% em type="red" %} 第一次触发等待3秒才执行,如频繁触发就从0开始直到你不按为止,待3秒才执行。 {% endem %}

<button class="btn">按钮</button>
<script>
    var btn = document.querySelector(".btn")
    function deBounce(fn, delayTime) {
        var timer
        return function () {
            if (timer) {
                clearTimeout(timer)
            }
            timer = setTimeout(function () {
                fn()
            }, delayTime)
        }
    }

    var handler = deBounce(function () {
        console.log(123);
    }, 3000)
    btn.addEventListener("click", handler)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

%/accordion%

# 3、优化版的防抖函数

[info] 优化版

防抖函数的问题,频繁的延迟会导致用户迟迟得不到响应。优化版的防抖函数本质上还是节流函数。

<button id="div"></button>
var div = document.querySelector("div");

function betterDebounce(fn, time) {
  var timer;
  var last = 0;
  return function () {
    var now = +new Date();

    if (now - last < time) {
      clearTimeout(timer);

      timer = setTimeout(function () {
        last = now;
        fn.apply(this, arguments);
      }, time);
    } else {
      clearTimeout(timer);
      last = now;
      fn.apply(this, arguments);
    }
  };
}
var clickHandler = betterDebounce(function () {
  console.log("触发了");
}, 3000);

div.onclick = clickHandler;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

%accordion% 优化版的防抖函数🚀 %accordion%

{% em type="red" %} 第一次触发就执行,如频繁触发就从0开始直到你不按为止,才执行 {% endem %}

<button class="btn">按钮</button>
<script>
    var btn = document.querySelector(".btn")
    function deBounce(fn, delayTime) {
        var timer
        var endTime = 0
        return function () {
            var nowTime = +new Date()
            if (nowTime - endTime < delayTime) {
                clearTimeout(timer)
                endTime = nowTime //清除计时器
                timer = setTimeout(function () {
                    fn()
                }, delayTime)
            } else {
                clearTimeout(timer)
                endTime = nowTime //清除计时器
                fn()
            }
        }
    }
    var handler = deBounce(function () {
        console.log(123);
    }, 3000)
    btn.addEventListener("click", handler)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

%/accordion%

编辑 (opens new window)
上次更新: 2022/04/24, 13:33:34
22jQuery
24面向对象

← 22jQuery 24面向对象→

最近更新
01
课件-react路由-V6
01-22
02
课件-国际化
01-22
03
课件-redux-toolkit
01-22
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式