js学习笔记(1)-函数中的this

以下为我个人的学习笔记,是从我自己比较能够理解的方面对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])
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值