手写实现call,apply,bind函数

目录

前言

call()的使用方法以及实现

1.call()使用方法

2.call()手写实现

apply()的使用方法以及实现

1.apply()使用方法

2.apply()手写实现

 bind()的使用方法以及实现

1.bind()的使用方法

2.bind()的手写实现


 

前言

之前在复习this指向时,提到过call()、apply()、bind()都可以用来改变函数的this指向,但在使用方法上有一些不同,所以本文主要简单论述一下三者的使用方法以及如何手写实现这三个方法。

call()的使用方法以及实现

1.call()使用方法

call方法在使用时,会直接执行调用的函数,传入的参数第一个为this指向的对象(若为空则指向window,严格模式下指向undefined),剩余参数(以参数列表的形式传入)都会作为当前调用函数的参数使用。具体使用方式如下:

 fn.call(thisArg,args1,args2,args3,...)相当于thisArg.fn(arg1,arg2,arg3,...)

具体例子: 

let obj={
            name:"张三"
        }
        function say(age){
            console.log("我的名字是"+this.name+"年龄"+age);
        }
        say.call(obj,13)//this指向obj,13作为参数传入函数say()中

var name="李四"
        let obj={
            name:"张三"
        }
        console.log(window);
        function say(age){
            console.log("我的名字是"+this.name+"年龄"+age);
        }
        say.call(null,13)//this指向window,13作为参数传入函数say()中

输出结果

240e072a7c7345a793372d466e26a26e.png1609d1da338f4d0c96d540da17817d14.png

2.call()手写实现

call()方法绑定在Function的prototype上,手写实现根据以下几点:

1.判断调用方法者是否为函数,如果不是则不可

2.接收的参数第一个为this指向的对象,其余为参数

3.第一个参数为空默认指向window,其余参数为空默认传空数组

4.使用时,会直接执行调用的函数将参数传入,调用完直接删除防止污染

5.函数要有返回值

具体实现:

Function.prototype.myCall = function (thisArg, ...args) {
	if (typeof this !== 'function') { //this指的是调用call()的原函数
    	throw new Error(`${this} must be a function`)
    }
    const fn = this;//定义一个属性保存原函数
    thisArg = thisArg == null ? window : Object(thisArg);//判断this新指向的对象是否为空
    args = args || [];//判断剩余参数,若为空默认为空数组
    thisArg.fn = fn;//将原函数保存为新对象的一个属性
    const result = thisArg.fn(...args);//利用解构语法调用这个函数,并将传入的参数传递给它
    delete thisArg.fn;//最后删除 thisArg 对象上的 fn 属性,防止污染
    return result;//返回结果
}

apply()的使用方法以及实现

1.apply()使用方法

apply方法在使用时,会直接执行调用的函数,传入的参数第一个为this指向的对象(若为空则指向window,严格模式下指向undefined),剩余参数(以数组的形式传入)都会作为当前调用函数的参数使用。具体使用方式如下: 

fn.apply(thisArg,[args1,args2,args3,...])相当于thisArg.fn([arg1,arg2,arg3,...])

具体例子

 let obj={
            name:"张三"
        }
        function say(age,a){
            console.log("我的名字是"+this.name+"年龄"+age+a);
        }
        say.apply(obj,[1,1])//this指向obj,数组[1,1]作为参数传入,被参数age、a接收

输出结果:

a1464e0251844162a443ddc6d1ae420b.png

2.apply()手写实现

apply()实现方法与call()相同,都是绑定在Function的prototype上,只是传入的参数方式不同,apply()要以数组方式传入,手写实现根据以下几点:

1.判断调用方法者是否为函数,如果不是则不可

2.接收的参数第一个为this指向的对象,其余为参数,并且要判断传入的是否为数组

3.第一个参数为空默认指向window,其余参数为空默认传空数组

4.使用时,会直接执行调用的函数将参数传入,调用完直接删除防止污染

5.函数要有返回值

具体实现:

Function.prototype.myApply = function (thisArg, args) {
	if (typeof this !== 'function') {
    	throw new Error(`${this} must be a function`)
    }
	var fn = this;
    thisArg = thisArg == null ? window : Object(thisArg);
    args = Array.isArray(args) ? args : [];//判断传入的参数是否为数组类型
    thisArg.fn = fn;
    const result = thisArg.fn(...args);
    delete thisArg.fn;
    return result;
}

 bind()的使用方法以及实现

1.bind()的使用方法

bind方法在使用时,会生成新的函数,传入的参数第一个作为新函数为this指向的对象(若为空则指向window,严格模式下指向undefined),剩余参数(以参数列表的形式传入,但是这个参数列表可以分多次传入)都会作为新函数的参数使用。具体使用方式如下:

let newFn=fn.bind(thisArg,args1,args2,args3,...)相当于thisArg.fn(arg1,arg2,arg3,...)

newFn(args4)调用新的函数时传入的参数会在输出时与在使用bind时传入的参数一起传给新函数并输出结果

具体例子:

let obj={
            name:"张三"
        }
        function say(a,b,c,d){
            console.log("我的名字是"+this.name+"出生年份"+a+b+c+d);
        }
        let say1=say.bind(obj,2,0,0)//创建新函数,this指向obj
        say1(2)//新函数传入参数2

输出结果:

8d5d309913824664b8922d3c0ea5a9e1.png

2.bind()的手写实现

bind()方法绑定在Function的prototype上,手写实现根据以下几点:

1.判断调用方法者是否为函数,如果不是则不可

2.调用bind会返回一个新函数,这个新函数可以被调用,也可以被当作构造函数,使用new操作符,要做区分

3.注意新旧函数参数的合并

4.函数要有返回值

 具体实现:

Function.prototype.bind = function (thisArg, ...args) {
	if (typeof this !== 'function') {
    	throw new Error(`${this} must be a function`)
    }
  context = thisArg || window
  let fn = this
  return function newFn (...fnArgs) {//返回闭包,在里面判断原函数是作为构造函数还是普通函数使用
    let res
    // 要考虑新函数是不是会当作构造函数
    if (this instanceof newFn) {
      // 如果是构造函数则调用new 并且合并参数args,fnArgs
      res = new fn(...args, ...fnArgs)
    } else {
      // 当作普通函数调用,通过 call 方法将原函数fn指向thisArg,并合并参数 args 和 fnArgs。
      res = fn.call(thisArg, ...args, ...fnArgs)
    }
    return res
  }
}

参考文档:【【详解】如何手写实现call、apply、bind - CSDN App】http://t.csdnimg.cn/Zvoru

 

### 手写实现 JavaScript 中的 `bind`、`call` 和 `apply` 方法 在 JavaScript 中,`bind`、`call` 和 `apply` 是 `Function` 原型上的方法,用于改变函数执行时的上下文(即 `this` 的指向),并控制参数的传递方式。以下分别对这三个方法进行手写实现,并结合引用中的代码进行说明。 --- #### 1. 手写实现 `call` 方法 `call` 方法允许调用一个函数,并指定其 `this` 值和一系列参数(以逗号分隔的形式传入)。 ```javascript Function.prototype.myCall = function(context, ...args) { if (typeof this !== 'function') { throw new TypeError('Function.prototype.myCall called on non-function'); } context = context !== null && context !== undefined ? Object(context) : window; const fnKey = Symbol('fn'); context[fnKey] = this; const result = context[fnKey](...args); delete context[fnKey]; return result; }; ``` 该实现中,首先判断调用 `myCall` 的对象是否为函数,然后处理传入的 `context`,将其转换为对象类型,防止传入 `null` 或 `undefined` 时出错。接着,使用 `Symbol` 创建一个唯一的属性名,将当前函数绑定到该属性上,最后调用函数并删除临时属性。 --- #### 2. 手写实现 `apply` 方法 `apply` 方法与 `call` 类似,不同之处在于它接受两个参数:第一个是 `this` 值,第二个是一个数组(或类数组对象),数组中的元素将作为参数传递给函数。 ```javascript Function.prototype.myApply = function(context, payload) { if (typeof this !== 'function') { throw new TypeError('Function.prototype.myApply called on non-function'); } context = context !== null && context !== undefined ? Object(context) : window; const fnKey = Symbol('fn'); context[fnKey] = this; let result; if (payload && Array.isArray(payload)) { result = context[fnKey](...payload); } else { result = context[fnKey](); } delete context[fnKey]; return result; }; ``` 此实现与 `myCall` 类似,但 `payload` 参数需要是一个数组,若传入的 `payload` 为 `null` 或 `undefined`,则直接调用函数而不传递参数。 --- #### 3. 手写实现 `bind` 方法 `bind` 方法用于创建一个新的函数,在调用时设置 `this` 值为指定的对象,并可预设一部分参数。 ```javascript Function.prototype.myBind = function(context, ...bindArgs) { if (typeof this !== 'function') { throw new TypeError('Function.prototype.myBind called on non-function'); } const self = this; return function(...callArgs) { return self.apply(context, bindArgs.concat(callArgs)); }; }; ``` 该实现中,`myBind` 返回一个新的函数,这个新函数在调用时会使用 `apply` 来调用原始函数,并将绑定时和调用时的参数合并传递给原始函数。 --- ### 使用示例 以下是一些使用上述手写方法的示例: ```javascript function sum(num1, num2) { console.log(num1 + num2, this); return num1 + num2; } // 使用 myCall sum.myCall({ name: 'Frank' }, 10, 20); // 输出 30 和 { name: 'Frank' } // 使用 myApply sum.myApply({ name: 'Frank' }, [10, 20]); // 输出 30 和 { name: 'Frank' } // 使用 myBind function showInfo(age, hobby) { console.log(`${this.name} is ${age} years old and likes ${hobby}.`); } const person = { name: 'Frank' }; const boundShowInfo = showInfo.myBind(person, 30); boundShowInfo('hiking'); // 输出: "Frank is 30 years old and likes hiking." ``` --- ### 注意事项 - 在实现 `myCall` 和 `myApply` 时,需要注意 `this` 的指向是否正确,并确保传入的参数格式符合预期。 - `myBind` 返回的是一个函数,因此在调用时可以继续传递参数,这些参数会在绑定时的参数之后传递给原始函数。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值