以下为我个人的学习笔记,是从我自己比较能够理解的方面对this进行的解读,可能会有误解或不够全面。
this是什么
this在JS中是一个“指针型变量”,它动态指向当前函数的运行环境,即代指当前函数的运行环境。
普通函数中的this: 谁调用指向谁
// 全局
function cool(){
console.log(this)
}
cool() // window,相当于window.cool()
// 函数中的this
var obj = {
cool: function(){
console.log(this)
}
}
obj.cool() // obj
那么为什么会存在this指向不对的问题呢?
var obj = {
cool: function(){
console.log(this)
}
}
setTimeout(obj.cool,200) // window
// 此时并没有直接执行obj.cool函数,实际效果其实如下:
setTimeout(function(){console.log(this), 200})
这里就涉及的JS的异步任务了,真正执行定时器中的函数时,该函数在全局环境中执行,即this指向window。
var obj = {
cool: function(){
setTimeout(function(){
console.log(this)
},200)
}
}
obj.cool() // window而不是obj
为了解决this指向不正确的问题,我们常常会另外定义一个变量来存储当前的this,这种方法实际上是使用了语法作用域和闭包,通过self确定了当前的作用域为obj,又通过闭包使得真正执行函数时依然能够访问obj:
var obj = {
cool: function(){
var self= this
setTimeout(function(){
console.log(self)
},200)
}
}
obj.cool() // obj
箭头函数中的this:指向于函数作用域所用的对象
实际上箭头函数没有自己的this,他的this是继承于他所定义位置的对象的this,因此箭头函数的this指向在定义之前就已经确定并无法修改:
var obj = {
cool: () => {
console.log(this)
}
}
obj.cool() // window
可以简单理解为箭头函数的this就是他所在定义对象的this,上例代码中,cool函数输出的this就可以当作是obj的this,而obj的this即为window,所以最终输出的this就是window。
改变this指向的方法
我们常用的改变this的方法有三个:apply、call、bind:
call()
call(a, b, c)
方法接收三个参数,第一个是this指向,后两个是传递给函数的实参,可以是数字,字符串,数组等类型的数据类型都可以:
var obj = { }
function fn(n){
console.log(this, n)
}
fn(1) // window, 1
fn.call(obj, 1) // obj, 1
apply()
apply(a, [b])接收两个参数,第一个是this指向,后一个参数只接受数组,apply把需要传递给fn()的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn()一个个的传递。
var obj = { }
function fn(n){
console.log(this, n)
}
fn(1) // window, 1
fn.apply(obj, [1]) // obj, 1
bind()
bind(a, b, c)
入参和call一致,执行bind后会生成一个改变了this指向的函数:
var obj = {
cool: function(){
console.log(this)
}
}
obj.cool() // obj
function fn(n){
console.log(this, n)
}
fn(1) // window, 1
fn.bind(obj, 1) // 不执行fn函数,相当于新声明了一个this指向为obj的函数
fn.bind(obj, 1)() // 改变指向this指向并执行
bind常常用在改变this指向后不需要立即执行的场景,例如一个需要改变this指向的点击函数:
var obj = { }
function fn(n){
console.log(this, n)
}
let oBox = document.createElement('div')
// 如果使用call,那么声明时即会执行fn函数, 可以重新声明一个函数,在内部进行call操作
// oBox.addEventListener('click',fn.call(obj,1))
oBox.addEventListener('click',function(){
fn.call(obj,1)
})
// bind就可以实现点击时再执行改变了指向的函数
oBox.addEventListener('click',fn.bind(obj,1))
实现call、apply、bind方法
call、apply方法实现
call、apply两者实现逻辑一致,只是他们的入参处理不一致。
Function.prototype.changeThis = function(thisArg){
if(typeof this !=='function'){
throw TypeError(this + 'is not a function')
}
// 为指向对象添加被调用函数
thisArg.soucreFunc = this
// 获取函数入参
const args = [...arguments].slice(1) // call方法入参处理
// const args = arguments[1] // apply 方法入参处理
// 执行该函数
const res = thisArg.soucreFunc(...args)
// 删除该函数,防止污染
delete thisArg.soucreFunc
// 返回执行结果
return res
}
bind方法实现
Function.prototype.myBind = function(thisArg){
if(typeof this !=='function'){
throw TypeError(this + 'is not a function')
}
const self = this // 保存原函数
const args = [...arguments].slice(1) // 获取函数入参
return function(){
// 为指向对象添加被调用函数
thisArg.soucreFunc = self
const res = thisArg.soucreFunc(...args)
// 删除该函数,防止污染
delete thisArg.soucreFunc
return res
}
// 上面更改指向的代码也可以直接调用apply来实现
// return self.apply(thisArg, [...args])
}