前端知识储备(一)“this”

this

知识线
什么是 this(对象) => 不同情况下的 this 指向(4 种) => 如何改变 this 指针(3 种) => 三者的共同点
与不同点(传参、执行) => call、apply、bind 源码实现

1.什么是 this 指针?

答: this 就是一个对象。不同情况下 this 指向的不同,有以下几种情况。

2.各种情况下的 this 指向问题:

答:

  • 对象调用, this 指向该对象(前边谁调用 this 就指向谁)。
var obj = {
		name:'小鹿',
		age: '21',
		print: function(){
		console.log(this)
		console.log(this.name + ':' + this.age)
	}
}
// 通过对象的方式调用函数
obj.print(); // this 指向 obj
  • 直接调用的函数, this 指向的是全局 window 对象。
function print(){
	console.log(this);
}
// 全局调用函数
print(); // this 指向 window
  • 通过 new 的方式, this 永远指向新创建的对象。
function Person(name, age){
	this.name = name;
	this.age = age;
	console.log(this);
}
var xiaolu = new Person('Deer',22); // this = > xaiolu
  • 箭头函数中的 this
const obj = {
a:()=>{
	console.log(this);
	}
}
// 对象调用箭头函数
obj.a(); // window

3.如何改变this指向,以及不同方式的相同点和不同点:

答:通过调用函数的:call、apply、bind三种方式改变this指向

var obj = {
	name:'Deer',
	age:'2',
	adress:'宇宙银河系地球'
	}
function print(){
	console.log(this); // 打印 this 的指向
}
// 通过 call 改变 this 指向,打印出来是obj的json
print.call(obj,1,2,3);
// 通过 apply 改变 this 指向,打印出来是obj的json
print.apply(obj,[1,2,3]);
// 通过 bind 改变 this 的指向,打印出来是obj的json
let fn = print.bind(obj,1,2,3);
fn();
  • 共同点:
    功能角度:三者都能改变 this 指向,且第一个传递的参数都是 this 指向的对象。
    传参角度:三者都采用的后续传参的形式。
  • 不同点:
    传参方面call 的传参是单个传递的(试了下数组,也是可以的),而 apply 后续传递的参
    数是数组形式(传单个值会报错),而 bind 没有规定,传递值和数组都可以。
    执行方面callapply 函数的执行是直接执行的,而 bind 函数会返回一个函数,然后我
    们想要调用的时候才会执行。

如果我们使用上边的方法改变箭头函数的 this 指针,会发生什么情况呢?能否进行改变

答:由于箭头函数没有自己的 this 指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参
数(不能绑定 this ),他们的第一个参数会被忽略。

4.call、apply、bind 实现(内部实现)

4.1 手写call

  • 首先 context 为可选参数,如果不传的话默认上下文为 window ;
  • 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数;
  • 因为 call 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来;
  • 然后调用函数并将对象上的函数删除。
// this 为调用的函数
// context 是参数对象
Function.prototype.myCall = function(context){
// 判断调用者是否为函数
	if(typeof this !== 'function'){
		throw new TypeError('Error')
	}
// 不传参默认为 window
	context = context || window
// 新增 fn 属性,将值设置为需要调用的函数
	context.fn = this
// 将 arguments 转化为数组将 call 的传参提取出来 [...arguments]
	const args = Array.from(arguments).slice(1)
// 传参调用函数
	const result = context.fn(...args)
// 删除函数
	delete context.fn
// 返回执行结果
	return result;
}

4.2 手写apply

  • 首先 context 为可选参数,如果不传的话默认上下文为 window
  • 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数
  • 因为 apply 传参是数组传参,所以取得数组,将其剥离为顺序参数进行函数调用
  • 然后调用函数并将对象上的函数删除
// 手写一个 apply 方法
Function.prototype.myApply = function(context){
// 判断调用者是否为函数
if(typeof this !== 'function'){
	throw new TypeError('Error')
}
// 不传参默认为 window
context = context || window
// 新增 fn 属性,将值设置为需要调用的函数
context.fn = this
// 返回执行结果
let result;
// 判断是否有参数传入
if(arguments[1]){
	result = context.fn(...arguments[1])
}else{
	result = context.fn()
	}
// 删除函数
	delete context.fn
// 返回执行结果
	return result;
}
// 普通函数
function print(age,age2,age3){
	console.log(this.name+" "+ age + " "+ age2+" "+age3);
}
// 自定义对象
var obj = {
	name:'小鹿'
}
// 调用函数的 call 方法
print.myApply(obj,[1,2,3])

4.3 手写bind

  • 判断调用者是否为函数。
  • 截取参数,注意:这里有两种形式传参。
  • 返回一个函数,判断外部哪种方式调用了该函数(new | 直接调用)
// 手写一个 bind 函数
Function.prototype.myBind = function (context) {
// 判断调用者是否为函数
if(typeof this !== 'function'){
	throw new TypeError('Error')
}
// 截取传递的参数
const args = Array.from(arguments).slice(1)
// _this 指向调用的函数
const _this = this;
// 返回一个函数
return function F(){
// 因为返回了一个函数,我们可以 new F(),所以需要判断
// 对于 new 的情况来说,不会被任何方式改变 this
	if(this instanceof F){
			return new _this(...args,...arguments)
		}else{
			return _this.apply(context,args.concat(...arguments))
		}
	}
}
// 普通函数
function print(){
// new 的方式调用 bind 参数输出换做 [...arguments]
	console.log(this.name);
}
// 自定义对象
var obj = {
	name:'小鹿'
}
// 调用函数的 call 方法
let F = print.myBind(obj,1,2,3);
// 返回对象
let obj1 = new F();
console.log(obj1);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值