JS函数式编程

2021/12/9 JavaScript柯里化arguments纯函数

# 实现apply、call、bind

# call函数的实现

//给所有函数添加一个oncall方法
Function.prototype.oncall = function (thisArg,...args) {
    
    //在这里获取需要调用的函数 foo,因为是隐式绑定,this指向的就是函数本身。
    //...arg是剩余参数
    var fn = this
    
    //将thisArg转成对象类型,进行判断,如果是null等就绑定为window
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
    
    //通过thisArg进行调用,隐式绑定将this绑定为thisArg
    thisArg.fn = fn
    result = thisArg.fn(...args)//这里...是展开
    
    //返回结果
    return result
    delete thisArg.fn
}

function foo() {
    console.log("foo被调用了",this)
}

function sum(num1,num2) {
    console.log( this,num1 + num2)
}

sum.oncall("aaa",1,2) //可以看作一个隐式绑定,这时的this就是函数本身
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

# apply函数的实现

//给所有函数添加一个onapply方法
Function.prototype.onapply = function (thisArg,argArray) {
    //获取到要执行的函数
    var fn = this
    
    //对thisArg进行处理
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
    
    //执行函数,绑定this
    thisArg.fn = fn
    
    //判断argArray有没有值
    //argArray = argArray ? argArray : []
    argArray = argArray || []
    result = thisArg.fn(...argArray)//这里...是展开
    delete thisArg.fn
    //返回结果
    return result
    
}

function foo() {
    console.log("foo被调用了",this)
}

function sum(num1,num2) {
    console.log( this,num1 + num2)
}

foo.onapply("aaa")
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

# bind函数的实现

Function.prototype.onbind = function(thisArg,...argArray){
  //返回一个新函数
  var fn = this
  //对this进行判断,绑定this
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
  function proxyFn(...args){
    //通过thisArg进行调用,隐式绑定将this绑定为thisArg
    thisArg.fn = fn
    //解决参数问题 对两个参数进行合并
    var finalArgs = [...argArray,...args]
    var result = thisArg.fn(...finalArgs)
    delete thisArg.fn
    //返回结果
    return result
  }
  return proxyFn
}

function sum(num1,num2,num3,num4){
    console.log(num1,num2,num3,num4,this)
}

//bind的参数可以分别传递
var newSum = sum.onbind("abc",10,20)

newSum(30,40)
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

# arguments

argument 是一个对应于 传递给函数的参数类数组(array-like)对象

function foo(num1,num2,num3){
    console.log(num1,num2,num3)
}
//参数会被放到一个类数组中

foo(10,20,30,40)

1
2
3
4
5
6
7

# aguments操作

# 获取参数长度:aguments.length

function foo(num1,num2,num3){
    console.log(arguments.length)//3
}

foo(10,20,30)

1
2
3
4
5
6

# 获取某一个参数:arguments[i]

function foo(num1,num2,num3){
    console.log(arguments[0])//10
    console.log(arguments[1])//20
    console.log(arguments[2])//30
}

foo(10,20,30)

1
2
3
4
5
6
7
8

# 获取当前arguments所在的函数

function foo(num1,num2,num3){
    console.log(arguments.callee)
    //f foo(num,num2,num3){...}
}

foo(10,20,30)

1
2
3
4
5
6
7

# arguments 转 Array 类型

# 直接遍历

//遍历arguments
function foo(num1,num2,num3){
  var newArr = []
  for(var i = 0;i < arguments.length;i++){
      newArr.push(arguments[i] * 10)
  }
    console.log(newArr)
}

foo(10,20,30)

1
2
3
4
5
6
7
8
9
10
11

# 通过 slice()

  • 方法一
function foo(num1,num2,num3){
	console.log(Array.prototype.slice.call(arguments))
}

foo(10,20,30,40)

1
2
3
4
5
6
  • 方法二
function foo(num1,num2,num3){
	console.log([].slice.call(arguments))
}

foo(10,20,30,40)

1
2
3
4
5
6
# slice函数的实现
Array.prototype.onslice = function(start,end){

    var arr = this
    start = start || 0
    end = end || arr.length
    var newArr = []
    for(var i = start;i<end;i++){
        newArr.push(arr[i])
    }
    return newArr
}

function foo(num1,num2,num3){
    console.log(
        Array.prototype.onslice.call(arguments))
        //因为是通过call调用,所以this指向的就是arguments本身
    }

foo(10,20,30,40)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# ES6语法:Array.from()

function foo(num1,num2,num3){
	console.log(Array.from(arguments))
}

foo(10,20,30,40)

1
2
3
4
5
6

# 展开运算符

function foo(num1,num2,num3){
	console.log([...arguments])
}

foo(10,20,30,40)

1
2
3
4
5
6

# ES6推荐使用args

var foo = (num1,num2,...args) =>{
    console.log(args)//[30,40,50]
}

foo(10,20,30,40,50)
1
2
3
4
5

# 理解JavaScript纯函数

# 纯函数的定义

# 副作用的理解

# 纯函数的案例

# 数组的两个函数的对比

var names = ["aaa","bbb","ccc","ddd"]
var newNames1 = names.slice(0,3)

//slice()
console.log(newNames1) //[ 'aaa', 'bbb', 'ccc' ]
console.log(names) //[ 'aaa', 'bbb', 'ccc', 'ddd' ]

//splice()
var newNames2 = names.splice(2)
console.log(newNames2)[ 'ccc', 'ddd' ]	
console.log(names) [ 'aaa', 'bbb' ]
1
2
3
4
5
6
7
8
9
10
11
  • slice()函数不会修改原来的值,会返回一个新的函数,符合纯函数的要求。
  • splice()函数会修改原来数组对象本身,所以splice()不是一个纯函数。

# 纯函数的优势

# JavaScript柯里化

# 柯里化的定义

  • 只传递给函数一部分参数来调用他,让它返回一个函数去处理剩余的参数
  • 这个过程就称为柯里化。
function foo(m,n,x,y){
    return m + n + x + y
}
var fo = foo(10,20,30,40)
console.log(fo)

//对上面的函数进行柯里化

function bar(m){
    return function(n){
        return function(x){
            return function(y){
              return  m + n + x + y
            }
        }
    }
}

var ba = bar(10)(20)(30)(40)
console.log(ba)

//箭头函数写法

var bar = m => n => x => y => m + n + x + y

console.log(bar(10)(20)(30)(40))
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

# 柯里化的作用

# 让函数的职责单一

  • 在函数式编程中,我们其实希望一个函数处理的问题尽可能的单一,而不是将一大堆的处理过程交给一个函数来处理
  • 那么我们是否就可以将每次传入的参数在单一的函数中进行处理,处理完之后在下一个函数中再使用处理后的结果

# 逻辑复用

function makeAdder(count){
    return function(num){
        return count + num
    }
}

var adder5 = makeAdder(5)

result1 = adder5(10)
result2 = adder5(15)

console.log(result1)
console.log(result2)
1
2
3
4
5
6
7
8
9
10
11
12
13

# 柯里化函数的实现

function Currying(fn){
    function curried(...args){
        //判断当前已经接收的参数的个数,判断参数本省需要接收的参数是否已经一致了
        //1.当已经传入的参数 大于等于 需要的参数时 就执行函数
        if(args.length >= fn.length){
            //fn(...args)
            //fn.call(this,...args)
            return fn.apply(this,args)
        }else{
            //没有达到个数时,需要返回一个新的函数,继续来接收参数
            function curried2(...args2){
                //接收到参数之后,需要递归调用curried来检查函数的个数是否达到
                return curried.apply(this,[...args,...args2])
            }
            return curried2
        }  
    }
    return curried
}

function foo(m,n,x,y){
    return m + n + x + y
}

var fooo = Currying(foo)(1)(2)(3)

console.log(fooo(4))function Currying(fn){
    function curried(...args){
        if(args.length >= fn.length){
            return fn.apply(this,args)
        }else{
            function curried2(...args2){
                return curried.apply(this,[...args,...args2])
            }
            return curried2
        }  
    }
    return curried
}

function foo(m,n,x,y){
    return m + n + x + y
}

var fooo = Currying(foo)(1)(2)(3)

console.log(fooo(4))
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

# 组合函数

function double(num){
    return num * 2
}

function square(num){
    return num**2
}

var count = 10
var result =  square(double(count))
console.log(result)

function composeFn(m,n){
    return function(count){
       return n(m(count))
    }
}

var newFn = composeFn(double,square)
console.log(newFn(10))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Last Updated: 2022/2/7下午10:20:47