【JS知识点】——JS中的this指向问题

JS中的this指向问题

apply和call

apply和call的功能

使用一个指定的this值和单独给出的一个或多个参数来调用一个函数;允许为不同的对象分配和调用属于一个对象的函数/方法

call和apply区别

call和apply的作用是相同的,都是用来更改this指向的;唯一的区别就在于传参的格式不一样:

  • apply接受两个参数:
    1.第一个参数指定了函数体内this对象的指向
    2.第二个参数为一个带下标的参数集合(可以是数组或者类数组)
  • call接受的参数不固定:
    1.第一个参数指定了函数体内this对象的指向
    2.第二个参数及以后为函数调用的参数

因为在所有(非箭头)函数中都可以通过arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,它本身就是一个类数组,我们apply在实际使用中更常见一些。

注意:当使用call或者apply的时候,如果我们传入的第一个参数为null,函数体内的this会默认指向宿主对象,即在浏览器中则是window。

this的指向可以分为哪几种?

作为对象的方法调用

如果调用对象中的函数,则函数中的this指向该对象:

// 作为对象的方法使用
var obj = {
	a: 'obj中的a',
    fn: function () {
    	console.log(this === obj);//this指向obj对象
        console.log(this.a);
    }
};
obj.fn(); 
// 输出:true obj中的a

作为普通函数调用

当this作为普通函数调用时,this指向全局(window),即使函数嵌套,this依旧指向全局(window),闭包函数同理。

// 作为普通函数使用
window.name = 'window中的name';

function fn(){
	var name = "函数中的name"
    console.log(111, this.name);
}
fn();// window中的name
        
//嵌套函数依旧指向window
function fn1() {
	var name = "函数中的name"
    console.log(111, this.name);
    function fn2() {
    	var name = "函数中的name"
        console.log(222, this.name);
        function fn3() {
        	console.log(333, this.name);
        }
        fn3();// window中的name
    }
    fn2();// window中的name
};
fn1(); // window中的name

//闭包函数同理
function fn4() {
	var name = "函数中的name"
    console.log(111,this.name);
    return function fn5() {
    	var name = "函数中的name"
        console.log(222,this.name);
    }
};
fn4()(); // window中的name

一种较为特殊的情况:当使用一个变量存储对象中的方法时,此时变量的值其实是一个函数(对象中的方法),这个变量不在obj对象中,所以this指向全局(window)。即变量将对象中的方法单独提取出来调用,相当于普通函数的this指向。

window.name = 'window中的name'
var obj = {
	name: '对象中的name',
	fn1: function () {
		console.log(this.name);
    }
};
var fn = obj.fn1;
obj.fn1();// 对象中的name
fn(); // window中的name

构造器调用

JS中大部分函数都可以用于作为构造器对象。构造器与普通函数最大的区别是:调用的方式。
在构造器中,当new运算符调用函数时,总是返回一个对象,this通常也指向这个对象。

// 构造器调用
var Collection = function () {
	this.data = '构造器中的数据';
};
var obj = new Collection();//与普通函数的区别,this指向新创建的对象
obj.data; // 构造器中的数据

call 或 apply调用

call与apply会改变函数中的this指向

// call和apply使用
var obj={
	msg:"这是obj中的数据",
    getMsg:function(){
    console.log(this.msg);
    }
}
var obj2={
	msg:"这是obj2中的数据"
}
obj.getMsg();//这是obj中的数据
obj.getMsg.call(obj2,obj2.msg);//这是obj2中的数据
obj.getMsg.call(obj2,[obj2.msg]);//这是obj2中的数据

箭头函数中使用

箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this

// 箭头函数中使用
this.val = "全局数据";
var obj = {
	val: "obj中的数据",
	getVal: () => {
    	console.log(this.val);
    }
}
obj.getVal(); // 全局数据

//通过call或apply也不能改变箭头函数的this指向
var arrow={
	name:"lr",
    fn:function(){
    	return function(){
        	console.log(this.name)
    	}
	},
    fn1:function(){
       return ()=>{
       		console.log(this.name);
       }
    }
}
var obj11={name:"111"}
arrow.fn().call(obj11);//111
arrow.fn1().call(obj11);//lr

JS中更改this指向的方法

call

定义一个a函数,函数本身的this是指向windows
这里用call来改变this的指向,这样再次打印this指向的就是obj
call立即执行。简单理解为调用函数的方式,但是它可以改变函数的 this 指向 应用场景: 经常做继承.

语法:函数名.call(调用者, 参数1, …)

 function a(name,age){
	this.name = name
    this.age = age
    console.log(this);
    console.log(this.aaa);
}
const obj = {
    aaa:123123123
}
a.call(obj,'张三',21)//打印的是obj这个对象和123123123

apply

定义一个b函数,函数本身的this是指向windows
这里用apply来改变this的指向,这样再次打印this指向的就是[‘张三’,21]
apply 立即执行。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。应用场景: 经常跟数组有关系
语法:函数名.apply(调用者, [参数, …])

 function b(arr){
	this.arr = arr 
    console.log(this.arr);
}
const obj1 = {
    aaa:123123123
}
b.apply(obj1,['张三',21])//打印的是['张三',21]

bind

定义一个c函数,函数本身的this是指向windows
这里用bind来改变this的指向,这样再次打印this指向的就是obj2
bind 懒执行,但是能改变函数内部this 指向,返回的是原函数改变this之后产生的新函数 应用场景:不调用函数,但是还想改变this指向

注意:返回的是一个函数,需要接收函数并需要手动调用

语法:函数名.bind(调用者, 参数, …)

function c(name,age){
    this.name = name
    this.age = age
    console.log(this);
 }
 const obj2 = {}
 const cc = c.bind(obj2,'李四',22)
cc()//将c函数的this绑定到obj2上,返回的是带有name、age的obj2

关于this指向的笔试、面试题

请你谈一下改变函数内部this指针的指向函数有哪几种,他们的区别是什么?

  • 上文中有介绍
var name = 'window'

function Person(name) {
  this.name = name
  this.obj = {
    name: 'obj',
    foo1: function () {
      return function () {
        console.log(this.name)
      }
    },
    foo2: function () {
      return () => {
        console.log(this.name)
      }
    }
  }
}

var person1 = new Person('person1')
var person2 = new Person('person2')

person1.obj.foo1()()
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)

person1.obj.foo2()()
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)


上述代码的执行结果为:window window person2 obj person2 obj

person1.obj.foo1()(): 相当于是将foo1闭包函数拿到全局环境执行,此时的this指向全局

person1.obj.foo1.call(person2)(): 相当于复制了一个this指向person2的foo1函数,但其中的闭包函数还是在全局环境下执行,所以this指向全局

person1.obj.foo1().call(person2): 通过call修改了foo1函数中的returen函数的this指向,此时的this指向person2

person1.obj.foo2()(): foo2函数的返回值为箭头函数,箭头函数的this指向是继承而来的,所以顺着作用域链查找到this指向obj

person1.obj.foo2.call(person2)(): 相当与修改了foo2函数的this指向,foo2函数中的this指向person2,而其返回值中的箭头函数的作用域为父作用域中的this指向,即为this指向person2

person1.obj.foo2().call(person2): 对于箭头函数,还是指向其父作用域中的this,通过call改变不生效。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值