call
call
方法的第一个参数也是this
的指向,后面传入的是一个参数列表改变this
指向后原函数会立即执行,且此方法只是临时改变this
指向一次
当第一个参数为null
、undefined
的时候,默认指向window
(在浏览器中)
function fn(...args){
console.log(`this的指向为${this}`,args);
}
let obj = {
myName:"张三"
}
fn.call(obj,1,2); // this会变成传入的obj,传入的参数是一个参数列表;
fn(1,2) // this指向window
fn.call(null,[1,2]); // this指向window
fn.call(undefined,[1,2]); // this指向window
apply
apply
接受两个参数,第一个参数是this
的指向,第二个参数是函数接受的参数,以数组的形式传入
改变this
指向后原函数会立即执行,且此方法只是临时改变this
指向一次
当第一个参数为null
、undefined
的时候,默认指向window
(在浏览器中)
function fn(...args){
console.log(`this的指向为${this}`,args);
}
let obj = {
myName:"张三"
}
fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window
fn.apply(null,[1,2]); // this指向window
fn.apply(undefined,[1,2]); // this指向window
bind
bind方法和call很相似,第一参数也是this
的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)
改变this
指向后不会立即执行,而是返回一个永久改变this
指向的函数
function fn(...args){
console.log(`this的指向为${this}`,args);
}
let obj = {
myName:"张三"
}
const bindFn = fn.bind(obj,[1,2]); // this 会变成传入的obj ,bind不是立即执行
bindFn(1,2) // this指向obj
fn(1,2) // this指向window
手写apply
- 判断是否为undefined或null,如果是,则this指向的是window
- 给目标对象创建一个属性,将要调用的方法绑定到这个属性上
- 用arguments拿到传入的参数
- 直接运行刚刚创建的属性
- 删除这个属性
Function.prototype.myApply = function(context){
if(typeof context === "undefined" || context === null){
context = window;
}
context.fn = this //this指向的是apply方法的函数
let args = arguments[1] //拿到第一个参数
let result
if(args){
result = context.fn(...args) //运行一下
}else{
result = context.fn()
}
delete context.fn
return result
}
手写call
思路同apply基本相同
不一样的是要通过arguments取出除第一位之外的所有参数放到一个数组里,然后通过展开运算符给要执行的函数或方法传参
//往Function的原型对象上加myCall方法
Function.prototype.myCall = function(context){
//判断是否为undefined或null
if(typeof context === 'undefined' || context === null){
context = window
}
context.fn = this //给目标对象新建一个属性,绑定这个函数
let args = [...arguments].slice(1) //过arguments取出除第一位之外的所有参数放到一个数组
let result = context.fn(...args) ////运行一下
delete context.fn //删除目标对象上的fn属性
return result
}
手写bind
bind() 方法会创建一个新函数。 当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this, 之后的一序列参数将会在传递的实参前传入作为它的参数
要注意因为bind转换后的函数可以作为构造函数使用,此时this应该指向构造出的实例
Function.prototype.myBind = function(context){
if(typeof this !== 'function'){
throw new TypeError('error')
}
// 获取参数
const args = [...arguments].slice(1)
let _this = this;
return function fn(){
//判断是都被当做构造函数使用,根据调用方式,传入不同绑定值
return _this.apply(this instanceof fn? new fn(...arguments):context,args.concat(...arguments))
}
}
小结
- 三者都可以改变函数的this指向
- 三者的第一个参数都是this要指向的对象,如果没有这个参数,或者参数为undefined或null,则默认会指向全局window
- 三者都可以传参,apply接受的是数组,call和bind接收的是参数列表,bind的参数列表可以分为多次传入
- apply和call是立即执行,而bind是返回绑定后的那个this之后的函数