call、apply 及 bind 函数
涉及面试题:call、apply 及 bind 三者的作用与区别?函数内部实现是怎么样的?
作用
call、apply、bind作用是改变函数执行时的上下文,也就是改变函数运行时的this指向。
区别
apply
apply
接受两个参数, 第一个参数是 this
的指向,第二个参数是函数接受参数 数组的形式。原函数会立即执行。
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window
当第一个参数为null、undefined的时候,默认指向window(在浏览器中)
call
call
第一个参数是 this
的指向,后面的参数列表(可选多个),都是函数接收的参数。 原函数会立即执行。
function fn(...args){
console.log(this,args);
}
let obj = {
myname:"张三"
}
fn.call(obj,1,2); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window
当第一个参数为null、undefined的时候,默认指向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) // this指向obj
fn(1,2) // this指向window
小结:
从上面可以看到,apply、call、bind
三者的区别在于:
- 都可以改变函数的
this
指向 - 第一个参数都是
this
要指向的对象,如果如果没有这个参数或参数为undefined
或null
,则默认指向全局window
- 都可以传参,但是
apply
是数组,而call
、bind
是参数列表,apply
和call
是一次性传入参数,bind
可以分为多次传入 bind
是返回绑定this
之后的函数,apply
、call
则是立即执行,将函数执行结果返回
实现
Function.prototype.nCall = function (context) {
if (typeof this !== 'function') {
throw new TypeError('not a function')
}
context = context || window
context.fn = this
const args = [...arguments].slice(1)
const result = context.fn(...args)
delete context.fn
return result
}
Function.prototype.nApply = function (context) {
if (typeof this !== 'function') {
throw new TypeError('not a function')
}
context = context || window
context.fn = this
let result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
Function.prototype.nBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('not a function')
}
const _this = this
const args = [...arguments].slice(1)
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}