call: 调用一个函数时,指定this,并分别传入参数
js
Function.prototype.call(thisArg, arg1, arg2, /* ..., */ argN)
演示代码:
js
function sayName(...args) {
console.log(this.name, ...args);
}
let person = {
name: '张三',
};
sayName();
sayName.call(person, '参数1', '参数2');
自己实现call:
核心原理:把函数挂载 到要指向的对象上,执行这个函数,然后删掉这个临时属性
js
Function.prototype.call2 = function (thisArg, ...args) {
// 第一步:获取call绑定的对象
let context = thisArg ?? globalThis;
// 第二步:将被调用函数设置为对象的属性
let fnName = Symbol('fn');
context[fnName] = this;
// 第三步:调用函数
let result = context[fnName](...args);
// 第四步:删除属性
delete context[fnName];
// 第五步:返回结果
return result;
};
function sayName(...args) {
console.log(this.name, ...args);
}
sayName();
let person = {
name: '张三',
};
sayName.call2(person, '参数1', '参数2');
apply: 调用一个函数时,指定this,将参数封装为数组或者类数组传入
js
Function.prototype.call(thisArg, argsArray)
演示代码:
js
function sayName(a, b) {
console.log(this.name, a, b);
}
sayName('参数1', '参数2');
let person = {
name: '张三',
};
sayName.apply(person, ['参数1', '参数2']);
自己实现apply:
核心原理:把函数挂载 到要指向的对象上,执行这个函数,然后删掉这个临时属性
globalThis:
globalThis是 JS 官方的 "全局对象,览器里 →globalThis === window,Node 里 →globalThis === global
js
Function.prototype.apply2 = function (thisArg, argsArray) {
// 第一步:获取apply绑定的对象
let context = thisArg ?? globalThis;
// 第二步:判断第二个参数不传的情况
const args = argsArray ? [...argsArray] : [];
// 第三步:将被调用函数设置为对象的属性
let fnName = Symbol('fn');
context[fnName] = this;
// 第四步:调用函数
let result = context[fnName](...args);
// 第五步:删除属性
delete context[fnName];
// 第六步:返回结果
return result;
};
function sayName(a, b) {
console.log(this.name, a, b);
}
sayName('参数1', '参数2');
let person = {
name: '张三',
};
sayName.apply2(person, ['参数1', '参数2']);
bind: 创建一个新函数,并指定该新函数运行时的this,并将指定的参数一一插入到原函数参数的前面
js
let newFunc = Function.prototype.bind(thisArg, arg1, arg2, /* ..., */ argN)
演示代码:
js
function sayName(a, b, c, ...args) {
console.log(this.name, a, b, c, ...args);
}
let person = {
name: '张三',
};
let newSayName = sayName.bind(person, '参数1', '参数2', '参数3');
newSayName('参数4', '参数5', '参数6');
自己实现bind:
注意点:
bind用于创建一个新函数,故应该返回一个函数,第一个参数是被绑定对象,后续参数用剩余参数接收
bind返回的新函数若通过new关键字调用,则其this应该绑定到新创建的对象上而不是调用bind时指定的。所以需要通过new.target去判断函数的调用方式。
可以通过call()函数完成一部分bind函数的实现。
js
Function.prototype.bind2 = function (thisArg, ...args) {
// 第一步:获取bind绑定的对象
let context = thisArg ?? globalThis;
// 保存原函数
let fn = this;
return function (...newArgs) {
// 如果函数是通过 new 调用的,this 应该指向新创建的对象,而不是 thisArg
context = new.target ? this : context;
// 调用原函数
return fn.call(context, ...args, ...newArgs);
};
};
function sayName(...args) {
console.log(this === globalThis, this.name, ...args);
}
let person = {
name: '张三',
};
let newSayName = sayName.bind2(person, '参数1', '参数2');
newSayName('参数3', '参数4'); // false 张三 参数1 参数2 参数3 参数4
new newSayName('参数5', '参数6'); // true undefined 参数1 参数2 参数5 参数6
new.target
ES6新增new.target,用于判断当前函数是被new构造调用还是被普通调用。
被new构造调用时,new.target的值是构造函数本身
被当作普通函数调用时,new.target的值是undefined
js
function person(name, age) {
this.name = name;
this.age = age;
console.log(new.target);
}
const p1 = new person('summer', 18); // [Function: person]
person('summer', 18); // undefined