title: 函数柯里化
categories: Javascript
tag:
- 实现apply,call,bind
date: 2021-11-16 09:16:34
实现call, apply,bind
实现 call
Function.prototype.Dhcall = function (thisArg, ...args) {//获取需要被执行的函数var fn = this//对thisArg转换为对象类型,防止传入的是非对象类型thisArg = thisArg != null && thisArg != undefined ? Object(thisArg) : windowthisArg.fn = fnvar result = thisArg.fn(...args)//会多出来属性,所以要删掉delete thisArg.fnreturn result}//测试代码======================================function foo() {console.log('foo被执行', this)}function bar(num1, num2, num3) {console.log('bar被执行', this, num1, num2, num3)return num1 + num2 + num3}foo.Dhcall({ name: 'why' })var result = bar.Dhcall('abc', 10, 20, 30)console.log(result)
实现 apply
Function.prototype.Dhapply = function (thisArg, argsArray) {//获取到要执行的函数,也就是thisvar fn = this//处理绑定的thisArgthisArg = thisArg != null && thisArg != undefined ? Object(thisArg) : windowthisArg.fn = fnvar result//1. 可以用条件判断==================/*if (!argsArray) {result = thisArg.fn()} else {result = thisArg.fn(...argsArray)}*///2. 可以使用三元运算符呀=============/*argsArray = argsArray ? argsArray : []*///3. 可以使用逻辑argsArray = argsArray || []result = thisArg.fn(...argsArray)delete thisArg.fnreturn result}//测试代码====================================// 两个参数function sum(num1, num2) {console.log('sum被调用', this, num1, num2)return num1 + num2}// 一个参数function single(num1) {console.log('single被调用', this, num1)return num1}function none() {console.log('none被调用', this)}// 两个参数var result = sum.Dhapply('a', [10, 20])console.log(result)// 一个参数也必须是数组类型var result = single.Dhapply('a', [10])console.log(result)// 没有参数.那么Dhapply第二个参数是undefined,所以不能被迭代。会报错。var result = none.Dhapply('a')console.log(result)
实现 bind
这是原生的 bind 实现
function foo() {console.log('foo被执行', this)}function sum(num1, num2, num3, num4) {console.log(num1, num2, num3, num4)}//没有参数var bar = foo.bind('abc')bar()//有参数,在bind的时候传var newSum = sum.bind('aaa', 10, 20, 30, 40)newSum()//有参数,在调用的时候传var newSum = sum.bind('aaa')newSum(10, 20, 30, 40)//有参数,两边都传var newSum = sum.bind('aaa', 10, 20)newSum(30, 40)
下面手写 bind 进行实现
Function.prototype.Dhbind = function (thisArg, ...argArray) {//1. 获取到真实需要调用的函数var fn = this//2. 绑定thisthisArg = thisArg !== undefined && thisArg !== null ? Object(thisArg) : windowfunction proxyFn(...args) {thisArg.fn = fnvar finalArgs = [...argArray, ...args]var result = thisArg.fn(...finalArgs)delete thisArg.fnreturn result}return proxyFn}//测试代码================================================function sum(num1, num2, num3, num4) {console.log(this, num1, num2, num3, num4)return num1 + num2 + num3 + num4}var newSum = sum.Dhbind('abc', 10, 20)var res = newSum(30, 40)console.log(res) //100
认识 arguments
arguments 是一个 对应于 传递给函数的参数 的 类数组(array-like)对象。
默认会有一个 arguments,类数组对象中(长的像是一个数组,本质上是一个对象:arguments)
function foo(num1, num2, num3) {//默认会有一个arguments,类数组对象中(长的像是一个数组,本质上是一个对象:arguments)console.log(arguments)console.log(num1, num2, num3)}foo(10, 20, 30, 40, 50)
arguments 操作
array-like 意味着它不是一个数组类型,而是一个对象类型:
- 但是它却拥有数组的一些特性,比如说 length,比如可以通过 index 索引来访问;
- 但是它却没有数组的一些方法,比如 forEach、map 等;
常见的对 arguments 的操作有三个
- 获取参数的长度
console.log(arguments.length)
- 根据索引值获取某一参数
console.log(arguments[2])console.log(arguments[3])console.log(arguments[4])
- callee 获取当前 arguments 所在的函数
console.log(arguments.callee)
所以为了可以使用数组方法,我们把 arguments 转换为数组
//把arguments转换为数组.slice全部放到数组里面,然后返回这个新数组var newArr = Array.prototype.slice.call(arguments)console.log(newArr)
那么 Array 中 slice 的实现原理
//这是slice原理Array.prototype.Dhslice = function (start, end) {start = start || 0end = end || arr.lengthvar arr = thisvar newArray = []for (let i = start; i < end; i++) {newArray.push(arr[i])}return newArray}var newArray = Array.prototype.Dhslice.call(['a', 'b', 'c', 'd'], 1, 4)console.log(newArray) // [10, 20, 30, 40, 50]
把 arguments 转换为数组另外的方法
//ES6语法var newArr = Array.from(arguments)console.log(newArr)

//展开运算符var newArr = [...arguments]console.log(newArr)
注意,箭头函数没有 arguments
箭头函数是不绑定 arguments 的,所以我们在箭头函数中使用 arguments 会去上层作用域查找
var foo = () => {console.log(arguments)}foo() //浏览器中没有arguments,node中有arguments
那我们在箭头函数中,应该使用剩余参数
