call,apply,bind混淆?那手写一个吧
在 js
里,this
指向一直是一个让人又爱又恨的问题,用的好的那真是一柄利刃,用不好的那网页飘红。我们在开发过程中经常会改变this
的指向,常用的就是call、apply 和 bind
。
简述 call、apply、bind 方法的区别
-
call
和apply
是改变后页面加载之后就立即执行,bind
不会立即执行,而是返回一个 新函数,适合回调场景使用。 -
call、apply、bind
第一个参数都是相同的(总是指函数运行时的this
) -
apply
的第二个参数是一个数组 -
call
和bind
的第二个参数是一 一列出的 -
call
和apply
只是临时的修改一次,也就是调用 call 和 apply 方法的那次;后面再次调用原函数的时候,它的指向还是原来的指向
function fn() {
console.log(this);
};
fn(); // window
const obj = {
name: 'constRen',
};
fn.call(obj); // {name: 'constRen'}
fn.apply(obj); // {name: 'constRen'}
fn(); // window
-
bind
是永久修改函数this
指向,但它修改的不是原函数;而是返回的新函数,新函数的this
被永远改变了
function fn() {
console.log(this);
};
fn(); // window
const obj = {
name: 'constRen',
};
let newfn = fn.bind(obj);
newfn(); // {name: 'constRen'}
fn(); // window
newfn() // {name: 'constRen'}
很多人都搞不清楚他们的用法,或者是容易混淆,我想了一个形象好记的:
1. call()
被借的对象.方法.call(借给谁, 参数1, 参数2, 参数3)
-
被借的对象===>就理解为 一个 有很多工具的人 -
方法 ===>就是工具 -
借给谁===> 就像你没有斧头,你想砍树,你可以向那个有很多工具的人 借用一下他的斧头
2. apply()
被借的对象.方法.call(借给谁, [参数1, 参数2, 参数3])
以上两个方法是一组的,它们只有参数不同,其他功能一样,都必须”马上执行”。
3. bind()
一般用于回调函数中,用于永久性的改变this的指向
被借的对象.方法.bind(借给谁, 参数1, 参数2, 参数3)
手写实现
实现Call
Function.prototype.myCall = function (ctx, ...args) {
// 1. 将方法挂载到 ctx 这个 this 就是那个方法!! 因为是 say 调用的 myCall
// 注意:有一种特殊情况就是 ctx 本身就有一个fn方法,这样就会被覆盖 这儿就需要一个第一无二的变量
const fn = Symbol();
ctx[fn] = this;
// 2. 调用这个方法
ctx[fn](...args);
// 3.调用完之后删除方法 不然对象就被改变了 多了一个方法
delete ctx[fn]
};
function say(...args) {
console.log('...args', ...args); // p1 p2 p3
console.log('this.name', this.name); // constRen
};
say.myCall({ name: "constRen" }, 'p1', 'p2', 'p3')
实现apply
Function.prototype.myApply = function (ctx, args = []) {
// 因为 apply 的第二个参数是一个数组 所以这儿需要限制
if (args && !Array.isArray(args)) {
throw '第二个参数必须是数组'
}
const fn = Symbol();
ctx[fn] = this;
ctx[fn](...args);
delete ctx[fn]
}
function say(...args) {
console.log('...args', ...args); // p1 p2 p3
console.log('this.name', this.name); // constRen
};
say.myApply({ name: "constRen" }, ['p1', 'p2', 'p3'])
实现bind
Function.prototype.myBind = function (ctx, ...args1) {
// 注意:bind 是返回一个新函数
return (...args2) => {
const fn = Symbol();
ctx[fn] = this;
ctx[fn](...args1.concat(...args2));
delete ctx[fn]
}
}
function say(...args) {
console.log('...args', ...args); // 'p1', 'p2', 'p3', 1, 23
console.log('this.name', this.name); // constRen
};
const sayFn = say.myBind({ name: "constRen" }, 'p1', 'p2', 'p3')
sayFn(1, 23) //
总结
我觉得这个就是一个借鸡生蛋的过程,借鸡生蛋大家都听说过吧?原理其实就和这个差不多
就三步
-
将方法挂载到对象上 (把鸡借过来) -
执行这个方法 (让鸡生蛋) -
从这个对象上删除刚刚挂载的方法 (把鸡还回去)
文章出自:https://juejin.cn/post/7225281021843030077
作者:constRen
原文始发于微信公众号(前端24):call,apply,bind混淆?那手写一个吧
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/216402.html