【前端手撕】call
call是改变this指向的方法,传入的参数是要指向的对象和函数需要的参数序列。
改变this指向还可以用apply和bind,区别如下:
| 方法 | 传参方式 | 执行时机 | 返回值 |
|---|---|---|---|
call | 参数序列(逐个传入) | 立即执行 | 函数执行的结果 |
apply | 参数数组(或类数组) | 立即执行 | 函数执行的结果 |
bind | 参数序列(逐个传入) | 返回新函数,稍后执行 | 绑定了this的新函数 |
代码
简易版
Function.prototype.callSimple = function (context, ...args) { context = context || window // 确认上下文,如果没有上下文就默认window context.fn = this // 把当前函数赋值给上下文的fn属性(临时属性) const res = context.fn(...args) // 调用函数,传参 delete context.fn // 删除临时属性 return res }健壮版
Function.prototype.call = function (context, ...args) { // 如果上下文是null或undefined,就默认window;否则用Object()转换为对象 // Object()包对象返回原对象,包原始类型返回对象包装类型。这是因为简单数据类型不能挂载属性,对象才可以 context = context !== null && context !== undefined ? Object(context) : window let tag = Symbol('call') // Symbol()创建一个唯一的符号值,避免与其他属性冲突/覆盖 context[tag] = this // 把当前函数赋值给上下文的tag属性(临时属性) const res = context[tag](...args) // 调用函数,传参。这里使用方括号是因为tag是一个符号值,不能用点号 return res }Tips
1. bind传参数可以先传一部分参数,返回新函数,下次再传剩下的。这种特性叫函数柯里化(Currying)
2. 如果bind返回的新函数被new构造调用了,this会失效。因为new的优先级高于bind(但也只有new比bind高)
function Person(name) { this.name = name; } const BoundPerson = Person.bind({ name: '默认' }); // 试图绑定 this const p = new BoundPerson('李四'); console.log(p.name); // 输出:李四因为new强行创建了一个新对象作为this,bind绑定的this被覆盖了。
