call、apply、bind作用是改变函数执行时的上下文,就是改变函数运行时的this指向
一、this
this关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象。this在函数执行过程中,this一旦被确定了,就不可以在更改。
1、this默认绑定
- 全局环境中,内部使用this关键字,指向Window对象
var name = "Tong"
function person(){
return this.name
}
console.log(person()) //Tong
只有函数运行在非严格模式下,默认绑定才能绑定到全局对象
2、this隐式绑定
- 对象方法中,this指这个上级对象
function test(){
console.log(this.x)
}
var obj = {}
obj.x = 1
obj.m = test
obj.m() //1
函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也是它的上一级对象
var o = {
a : 10
b:{
fn : function(){
console.log(this.a) //undefined
}
}
}
o.b.fn()
3、new绑定
通过构建new关键字生成一个实例对象,此时这个this指向这个实例对象
function fn(){
console.log(this) //指向obj
this.user = "xxx"
}
var obj = new fn()
obj.user //xxx
4、箭头函数中
箭头函数不绑定this,箭头函数中的this指向它所定义的位置,即定义箭头函数的作用域的this指向谁,this就指向谁
setTimeout(() => {
console.log(this) //window
}, 100)
5、显示修改
apply()、call()、bind()可以用来改变函数的调用对象,第一个参数表示改变后的调用这个函数的对象
二、call、apply、bind
1、call()方法
call 方法的第一个参数是this指向,后面传入的是一个参数列表,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){
console.log(this,args)//{myname:'Tong'} [1,2]
}
let obj ={
myname : "Tong"
}
fn.call(obj,1,2) //this会变成传入的obj,传入的参数必须是一个数组
fn(1,2) //[1,2] this指向window
fn.call(null,[1,2]) //this指向window
fn.call(undefined,[1,2]) //this指向window
实现call
Function.prototype.myCall = function(context){
if(typeof this !=='Function'){
throw new Error("not function!")
}
context = window||context
context.fn = this
let args = Array.from(arguments).slice(1)
let result = context.fn(...args)
delete context.fn
return result
}
2、 apply()方法
apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的显示传入,改变this指向后原函数立即执行。且此方法只是临时改变this指向一次
function fn(...args){
console.log(this,args)//{myname:'Tong'} [1,2]
}
let obj ={
myname : "Tong"
}
fn.apply(obj,1,2) //this会变成传入的obj,传入的参数必须是一个数组
fn(1,2) //[1,2] this指向window
fn.call(null,[1,2]) //this指向window
fn.call(undefined,[1,2]) //this指向window
实现apply()
Function.prototype.myApply = function(context){
if(typeof this !== 'Function'){
throw TypeError('not function!')
}
context = context || window
context.fn = this
if(arguments[1]){
let result = context.fn(...arguments[1])
}else{
result = context.fn()
}
delete context.fn
return result
}
3、bind()方法
bind方法与call很相似,第一个参数是this指向,后面传入的也是一个参数列表(参数列表分多次传入),改变this后不会立即执行,而是返回一个永久改变this指向的函数
function fn(...args){
console.log(this,args)//{myname:'Tong'} [1,2]
}
let obj ={
myname : "Tong"
}
const bindfn = fn.apply(obj) //this会变成传入的obj,bind不是立即执行
bindfn(1,2) //this指向obj
fn(1,2) //[1,2] this指向window
实现bind()
Function.prototype.myBind = function (context){
if(typeof this !== 'function'){
throw new TypeError("error")
}
//获取参数
const args = [...arguments].slice(1)
const fn = this
return Fn(){
//根据调入方式,传入不同的绑定值
return fn.apply(this instanceof Fn? new fn(...arguments):context,args.concat(...arguments))
}
}