call、apply、bind 的用法
let xw = { name : ‘小王‘, gender : ‘男‘, age : 24 , say : function() { console.log(this.name + ‘,性别‘ + this.gender + ‘,年龄‘ + this.age); } } let xh = { name : ‘小红‘, gender : ‘女‘, age : 20 } let xl = { name : ‘小蓝‘, gender : ‘男‘, age : 22 } xw.say(); // this还是原来的xw的 xw.say.call(xh); // this指向xh xw.say.apply(xl); // this指向xl xw.say.bind(xh)(); // this指向xh
let xw = { name : ‘小王‘, gender : ‘男‘, age : 24 , say : function(school , grade) { console.log(this.name + ‘,性别‘ + this.gender + ‘,年龄‘ + this.age + ‘,在‘ + school + "上" + grade); } } let xh = { name : ‘小红‘, gender : ‘女‘, age : 20 } let xl = { name : ‘小蓝‘, gender : ‘男‘, age : 22 } xw.say("实验小学","六年级"); xw.say.call(xh , "实验小学","六年级"); xw.say.apply(xl , ["实验小学","六年级"]); xw.say.bind(xh,"实验小学","六年级")(); xw.say.bind(xl)("实验小学","六年级");
call
、apply
、bind
的共同点都是为了解决改变this
的指向。作用都是相同的,只是传参的方式不同。
不同点:
-
call()
和apply()
是立即执行的,而bind()
是返回一个函数。 -
call()
可以传递多个参数,第一个参数和apply()
一样,是用来替换的对象,后面是参数列表。 -
apply()
最多只能有两个参数 —— 新this
对象和一个参数数组argArray
-
bind()
和其他两个方法的作用也是一致的,只是该方法会返回一个函数,并且可以通过bind()
实现 柯里化。
柯里化会在下一篇文章进行较为详细的介绍。
对于实现以下三个函数,可以从这几个方面进行考虑:
- 不传入第一个参数,那么默认为
window
。- 改变了
this
指向,让新的对象可以执行该函数。那么思路就可以变成给新的对象添加一个函数,然后在执行完以后删除。
Function.prototype.myCall = function( context ){ // 1. 判断有没有传入要绑定的对象,没有默认为window;如果是基本类型的话通过Object()方法进行转换 var context = Object(context) || window; // 2. 给context添加一个fn属性,值为this,也就是fn() context.fn = this; // 3. 保存返回值 let result = ‘‘; // 4. 取出传递的参数,第一个参数是this // 截取除第一个参数外剩余参数的方法 const args = [...arguments].slice(1); // const args = Array.prototype.slice.call(arguments , 1); // const args = Array.from(arguments).slice(1); // 5. 执行方法,传入参数 // ... 是es6的展开数组 result = context.fn(...args); // 6. 删除该属性 delete context.fn; // 7. 返回结果 return result; } // 测试用例 const obj = { value :‘hello‘ } function fn(name , age){ return { value : this.value , name , age } } let res = fn.myCall(obj , ‘LL‘ , 25); console.log(res) // { value: ‘hello‘, name: ‘LL‘, age: 25 }
Function.prototype.myApply = function( context , args ){ var context = Object(context) || window; context.fn = this; let result = ‘‘; // 4. 判断有没有传入第二个参数 args,如果传入就将第二个参数展开 if(!args){ // 没有传入,直接返回该函数 result = context.fn(); }else{ // 传入了,将参数展开 result = context.fn(...args); } delete context.fn; return result; } // 测试用例 const obj = { value :‘hello‘ } function fn(name , age){ return { value :this.value , name , age } } let res = fn.myApply(obj ,[ ‘LL‘ , 25]); console.log(res) // { value: ‘hello‘, name: ‘LL‘, age: 25 }
Function.prototype.myBind = function( context ){ // 1. 判断this是不是一个函数 if(typeof this !== ‘function‘){ // 不是函数,抛出错误 throw new Error(‘不是一个函数‘); } // 2. 暂存this const self = this; // 3. 获取传入的参数 // 拿到第一组参数,如果没传,是一个空数组 const args1 = [...arguments].slice(1); // 第二次调用bindFn const bindFn = function(){ // 获取第二个参数 const args2 = [...arguments]; // 将第一部分参数和第二部分参数合并到一起,进行返回 return self.apply(context , args1.concat(args2)); } return bindFn } // 测试用例 const obj = { value :‘hello‘ } function fn(name , age){ return { value :this.value , name , age } } let res = fn.myBind(obj)(‘HH‘ , 30); console.log(res)