区别
三者都可以改变函数的this对象指向
三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window
三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入
bind是返回绑定this之后的函数,apply、call 则是立即执行
使用
apply
apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
当第一个参数为null、undefined的时候,默认指向window(在浏览器中)
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
fn.apply(obj,[1,2]); //{myname: '张三'} [1, 2], this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) //window [1, 2], this指向window
fn.apply(null,[1,2]); //window [1, 2], this指向window
fn.apply(undefined,[1,2]); //window [1, 2], this指向window
callcall方法的第一个参数也是this的指向,后面传入的是一个参数列表,第一个参数为null、undefined的时候,默认指向window(在浏览器中)
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
fn.call(obj,1,2); //{myname: '张三'} [1, 2], this会变成传入的obj,传入的参数分开传;
fn.call(obj,[1,2]); //{myname: '张三'} [[1, 2]], this会变成传入的obj,传入一个数组默认当一个值
fn(1,2) //window [1,2], this指向window
fn.call(null,1,2); window [1,2], this指向window
fn.call(undefined,1,2); window [1,2], this指向window
bind
bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表
改变this指向后不会立即执行,而是返回一个永久改变this指向的函数
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
const bindFn = fn.bind(obj); // this 也会变成传入的obj ,bind不是立即执行需要执行一次
bindFn(1,2) //{myname:"张三"} [1,2], this指向obj
fn(1,2) //window [1,2], this指向window
const foot ={
apple:'苹果'
}
function eat(a,b){
console.log(a,b,this)
}
const bindEat = eat.bind(foot)
eat(1,2) // 1 2 Window
bindEat(1,2) //1 2 foot
eat.call(foot,1,2) //1 2 foot
/* apply参数已数组形式传递 */
eat.apply(foot,[1,2]) //1 2 foot
手写apply
function greet(msg) {
console.log(msg, this.name);
}
const obj = { name: "Alice" };
//手写apply
Function.prototype.myApply = function(context, argsArray = []) {
context = context || window;
const fnKey = Symbol();
context[fnKey] = this;
// 使用展开语法传递数组参数
const result = context[fnKey](...argsArray);
delete context[fnKey];
return result;
};
greet.myApply(obj, ["Hi"]); // 输出: Hi Alice
手写call
function greet(msg) {
console.log(msg, this.name);
}
const obj = { name: "Alice" };
//手写call
Function.prototype.myCall = function(context, ...args) {
// 处理 context 为 null 或 undefined 的情况
context = context || window;
// 防止属性名冲突,使用 Symbol 作为唯一键
const fnKey = Symbol();
// 将当前函数绑定到 context 上(this 指向调用 myCall 的函数)
context[fnKey] = this;
// 执行函数并保存结果
const result = context[fnKey](...args);
// 删除临时绑定的属性
delete context[fnKey];
return result;
};
greet.myCall (obj, "Hello"); // 输出: Hello Alice
手写bind
Function.prototype.myBind = function(context, ...bindArgs) {
const originalFunc = this;
// 返回一个绑定 this 的新函数
const boundFunc = function(...callArgs) {
// 判断是否通过 new 调用(this 是否为 boundFunc 的实例)
const isNewCall = this instanceof boundFunc;
// 如果是 new 调用,保持 this 指向新对象;否则绑定到 context
return originalFunc.apply(
isNewCall ? this : context,
[...bindArgs, ...callArgs]
);
};
// 维护原型链(避免修改原函数的 prototype)
if (originalFunc.prototype) {
boundFunc.prototype = Object.create(originalFunc.prototype);
}
return boundFunc;
};
const boundGreet = greet.myBind(obj, "Hey");
boundGreet(); // 输出: Hey Alice
function Person(name) {
this.name = name;
}
const BoundPerson = Person.myBind({});
const p = new BoundPerson("Bob");
console.log(p.name); // 输出: Bob(正确绑定到新对象)