文章目录

        • 一、区别:
        • 二、案例:
        • 三、实现:
            • 【1】call实现
            • 【2】apply实现
            • 【3】bind实现

一、区别:

call、apply、bind相同点:都是改变this的指向,传入的第一个参数都是绑定this的指向,在非严格模式中,如果第一个参数是nul或者undefined,会把全局对象(浏览器是window)作为this的值,要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined

call和apply唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组

bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)

二、案例:



三、实现:

【1】call实现

对象context想调用一个它没有的方法f 怎么办呢?f.call(context) 通过call来借用方法f ,怎么做到的呢

  1. 对象context添加f方法
  2. 对象context执行f方法
Function.prototype.myCall = function(context, ...arg) {// 如果第一个参数传入的是undefined和null,context为window对象context = context || window; // 为context对象添加函数barcontext.fn = this; // this:bar,this指向调用myCall的bar // context对象执行函数bar,并返回结果returncontext.fn(...arg); } // 测试一下var value = 2; var obj = {value: 1}function bar(name, age) {console.log(this.value);return {value: this.value,name: name,age: age}}bar.myCall(null); // 2this指向window console.log(bar.myCall(obj, 'kevin', 18)); //1this指向objObject { value: 1,name: 'kevin',age: 18}
【2】apply实现

apply和call唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组

Function.prototype.myApply = function(context, arg) {// 如果第一个参数传入的是undefined和null,context为window对象context = context || window; // context对象添加函数barcontext.fn = this; // this:bar,this指向调用myCall的函数bar // context对象执行函数bar,并返回结果let result = null;if (!arg) { // 没有传入数组result = context.fn();}else{// 传入了参数数组result = context.fn(...arg);} return result;} // 测试一下var value = 2; var obj = {value: 1}function bar(name, age) {console.log(this.value);return {value: this.value,name: name,age: age}} bar.myApply(null); // 2 console.log(bar.myApply(obj, ['kevin', 18])); // 1 传入的是数组['kevin', 18]
【3】bind实现

bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)
bind实现最为复杂,因为经过bind绑定过的函数,既可以被当作普通函数调用,又可以被当作构造函数调用

Function.prototype.myBind = function (context, ...args){// 如果第一个参数传入的是undefined和null,context为window对象context = context || window; // context对象添加函数Personcontext.fn = this; // this:Person,context.fn:Person,_this:Personlet _this = this; // bind返回的函数 const result = function (...innerArgs) { if (this instanceof _this ) { // this:a (new出来的实例对象) ,_this:Person// 为实例对象a添加Person方法this.fn = _this;// 实例对象a执行Person方法this.fn(...[...args,...innerArgs]);}else{// 普通函数被调用context.fn(...[...args,...innerArgs]);}} result.prototype = Object.create(this.prototype);// 为什加这一句?看原型图下面会解释return result;} // 测试// function Person(name, age) {// console.log(name); //'我是第一次参数传进来的name被args接收'// console.log(age); //'我是第二次参数传进来的age被innerArgs接收'// console.log(this); //构造函数this指向实例对象// }// // 构造函数原型的方法// Person.prototype.say = function() {// console.log(123);// } // let obj = {// objName: '我是obj传进来的name',// objAge: '我是obj传进来的age'// } // // bind 返回的函数 作为构造函数调用// let bindFun = Person.myBind(obj, '我是第一次参数传进来的name被args接收') // this:obj// let a = new bindFun('我是第二次参数传进来的age被innerArgs接收') // this:a// a.say() //123 // 测试let obj = {objName: '我是obj传进来的name',objAge: '我是obj传进来的age'} // 普通函数function normalFun(name, age) {console.log(name);//'我是第一次参数传进来的name'console.log(age); //'我是第二次参数传进来的age'console.log(this === obj);// trueconsole.log(this.objName);//'我是obj传进来的name'console.log(this.objAge); //'我是obj传进来的age'} // bind 返回的函数 作为普通函数调用let bindFun = normalFun.myBind(obj, '我是第一次参数传进来的name被args接收'); // this指向obj bindFun('我是第二次参数传进来的age被innerArgs接收');