细谈this指向

前言

this可以说是一个老生常谈的一个东西了,但是之前并没有对this的指向做个一个完整的总结,今天就来简单总结一下吧。

this概念

       this 表示当前对象的一个引用。在 js 中 this 不是固定不变的,它会随着执行环境的改变而改变。不管它发生什么改变,普通函数的this 的指向都永远只能看this是谁调用的,而不是在哪里定义的,谁调用的函数,函数体里的this就指谁。而箭头函数因为没有this,它的this是继承来的。一般也不可改变(但是我们可以通过其继承this的规则实现"改变"箭头函数的this指向)

this绑定规则

普通函数

调用绑定:

//函数直接调用和匿名函数的调用this指向window
console.log(this)//window


function foo(){
    console.log(this)
}
foo()//window


(function (){
   console.log(this) //window
})()


//嵌套函数调用
let obj={
    a:function(){
        console.log(this)
    }
}
obj.a()//window
//上边的调用方法相当于obj.a.call(obj)

其实还有一个setTimeout和 setInterval中的this这个里边的this指向会因为传入执行函数的方式不同而不同,在之后会单独说明一下

隐式绑定:

//隐式丢失

//方法一
function foo(){
    console.log(this)
}
let obj={
    foo:foo
}
let bar=obj.foo
obj.foo()//obj
bar()//window


//方法二
function foo(fn){
    fn()
}
function bar(){
    console.log(this)
}
let obj={
    bar:bar
}
foo(obj.bar)//window


//间接改变
function foo(){
    console.log(this)
}
let obj1={
    foo:foo
}
let obj2={
    
}
obj2.foo=obj1.foo
obj2.foo()//obj2

内置函数的绑定:

forEach(),map(),filter(),some(),every()//这些方法里边的回调函数的this指向都是window(相当于匿名函数)
//forEach()函数遍历数组
//map()函数与forEach()函数类似,但此方法会返回一个新的数组(不改变原数组)
//filter(),过滤到不符合条件的值返回一个新的数组
//some(),判断每一个元素是否有任意一个满足条件, 有的话返回true, 否则false
//every(),判断每一个元素是否全部满足条件, 有的话返回true, 否则false

new关键字绑定

let that=null
function Foo(){
    that=this
    console.log(this)
}
let foo=new Foo()
console.log(that===foo)

显示绑定:
使用三种方法可以改变this指向,其中需要额外注意一下bind()方法,具体的可以看我上一篇博客call、apply、bind三种方法的比较

//使用call函数、apply函数和bind函数
let obj1={
    foo:function(){
        console.log(this)
    }
}
let obj2={
    bar:function(){
        console.log(this)
    }
}
obj1.foo.call(obj2)//obj2
obj1.foo.apply(obj2)//obj2
obj1.foo.bind(obj2)()//obj2

call方法,apply方法和bind方法的区别

定时器的this指向

//匿名函数
setTimeout(function(){
   console.log(this)//window
},0)


//传入函数1
let obj={
    foo:function(){
        console.log(this)
    }
}
setTimeout(obj.foo,0)//window(相当于把foo这个函数传进去)

//传入函数2
let obj={
    foo:function(){
        console.log(this)
    }
}
setTimeout(obj.foo(),0)//obj(相当于执行obj.foo())
箭头函数

箭头函数并没有自己的this,它的this是通过继承来的(即根据它书写的位置的函数的执行上下文来确定)

//最外层的箭头函数的this
let foo=()=>{
    console.log(this)
}
foo()//window(这里的this指向并不能改变)



//函数里边的箭头函数
function foo(){
    let bar=()=>{
        console.log(this)//这里的this继承的是foo的this指向(this:箭头函数的上一次的爸爸)
        /* 解释上边的括号的话
        		箭头函数是bar这一层,上一层是foo,foo的爸爸是window
        	备注:这个规律使用于绝大多数情况,但仍要记住箭头函数的this是继承来的根据它的执行上下文来确定,随后会给大家举一个不满足这种规律的情况,并给大家进行说明
        */
    }
    bar()
}
let obj={}
foo()//window
foo.call(obj)/* obj(通过改变foo的this进而间接改变了里边箭头函数的this,而箭头函数的this还是那个位置的值,这也就是我最开始的时候说改变加引号的原因) */
//反例
let obj1={
    obj2:{
        foo:()=>{
            console.log(this)
        }
    }
}
obj1.obj2.foo()//window
/* 按照上边的规律,这里的this指向的不应该是obj1吗?为啥这里是window
解释:
	this产生于函数调用的时候,而箭头函数的this又是继承来的,他并没有可继承的上一级的爸爸,只能由大怨种window来兜底了
	或者大家可以在输出语句哪里打一个断点来看this的值是undefined
*/

严格模式下this的值是undefined

//小总结
const obj = {
	a: function () {
		console.log(this)
	},
	b() {
		console.log(this)
	},
	c: () => {
		console.log(this)
	},
	d: function () {
		return (() => {
 			console.log(this)
		})()
	},
	e: function () {
        console.log(this)
	},
	f: function () {
		return this.b
	},
	g: function () {
		console.log(this)
	},
	h: function () {
		return this.c
	},
	i: function () {
		// console.log(this)
		return () => {
			console.log(this)
		}
	}
}
obj.a()
obj.b()
obj.c()
obj.d()
obj.e()
obj.f()()
obj.g()
obj.h()()
obj.i()()
let a=obj.i()
a()
setTimeout(obj.i()(), 0)
setTimeout(obj.i, 0)
//答案
const obj = {
	a: function () {
		console.log(this)
	},
	b() {
		console.log(this)
	},
	c: () => {
		console.log(this)
	},
	d: function () {
		return (() => {
 			console.log(this)
		})()
	},
	e: function () {
        console.log(this)
	},
	f: function () {
		return this.b
	},
	g: function () {
		console.log(this)
	},
	h: function () {
		return this.c
	},
	i: function () {
		// console.log(this)
		return () => {
			console.log(this)
		}
	}
}
obj.a()//obj
obj.b()//obj
obj.c()//window
obj.d()//obj
obj.e()//obj
obj.f()()//window
obj.g()//obj
obj.h()()//window
obj.i()()//obj
let a=obj.i()
a()//obj
setTimeout(obj.i()(), 0)//obj
setTimeout(obj.i, 0)//o

this练习题

//练习题一
var obj = {
	a:1,
	c:2,
	say:function(a){
         console.log("this1: "+this)
		var sayA = function(a){
			console.log("this2: "+this)
			this.a = a
		}
        function sayC(){
            console.log("this3: "+this)
        }
		sayA(a)
		sayC()
	}
}
obj.say(3)
console.log(obj.a+"  "+obj.c)
console.log(window.a+"  "+window.c)
//练习题一答案:
var obj = {
	a:1,
	c:2,
	say:function(a){
         console.log("this1: "+this)
		var sayA = function(a){
			console.log("this2: "+this)
			this.a = a
		}
        function sayC(){
             console.log("this3: "+this)
         }
		sayA(a)
		sayC()
	}
}
obj.say(3)
/* 
this1:obj
this2:window
this3:window
*/
console.log(obj.a+"  "+obj.c)//1 2
console.log(window.a+"  "+window.c)//3 undefined
//练习题二
function Person(name,age){
      this.name = name
      this.age = age
}
var person1 = new Person("张三",18)
var person2 = Person("李四",12)
console.log(person1)
console.log(person2)
console.log(person1.name,person1.age)
console.log(window.name,window.age)
//练习题二答案
function Person(name,age){
      this.name = name
      this.age = age
}
var person1 = new Person("张三",18)
var person2 = Person("李四",12)
console.log(person1)//{name:张三,age:18}
console.log(person2)//undefined
console.log(person1.name,person1.age)//张三,18
console.log(window.name,window.age)//李四,12
//练习题三
function Test(a, b) {
    this.a = a
    this.b = b
    this.say = function (a, b) {
      	this.a = a
       	this.b = b
       	console.log(this)
        console.log(a, b)
     }
}
var obj1 = new Test(1, 1)
console.log(obj1.a, obj1.b)
obj1.say()
console.log(obj1.a, obj1.b)
var obj2 = { a: 2, b: 2 }
obj1.say(4, 4)
console.log(obj1.a, obj1.b)
obj1.say.call(obj2, 3, 3)
console.log(obj2.a + "--" + obj2.b)
//练习题三答案
function Test(a, b) {
    this.a = a
    this.b = b
    this.say = function (a, b) {
      	this.a = a
       	this.b = b
       	console.log(this)
        console.log(a, b)
     }
}
var obj1 = new Test(1, 1)
console.log(obj1.a, obj1.b)//1,1
obj1.say()//obj1,undefined,undefined
console.log(obj1.a, obj1.b)//undefined,undefined
var obj2 = { a: 2, b: 2 }
obj1.say(4, 4)//obj1,4,4
console.log(obj1.a, obj1.b)//4,4
obj1.say.call(obj2, 3, 3)//obj2,3,3
console.log(obj2.a + "--" + obj2.b)//3--3
//练习题四
obj = {
    func() {
        const arrowFunc = () => {
            console.log(this)
        } 
        return arrowFunc
    },
    _name: "obj",
}
obj.func()()
func = obj.func
func()()
obj.func.bind({ _name: "newObj" })()()
obj.func.bind()()()
obj.func.bind({ _name: "bindObj" }).apply({ _name: "applyObj" })()
//练习题四答案
let obj = {
    func() {
        const arrowFunc = () => {
            console.log(this)
        } 
        return arrowFunc
    },
    _name: "obj",
}
//箭头函数的this指向一直是上一层的爸爸(即func的this指向)
obj.func()()//obj
//func是一个全局遍历它的this指向的是window
func = obj.func
func()()//window
obj.func.bind({ _name: "newObj" })()()//{ _name: "newObj" }
obj.func.bind()()()//window
obj.func.bind({ _name: "bindObj" }).apply({ _name: "applyObj" })()//{ _name: "bindObj" }
//bind会返回绑定好this的函数供调用
//解释bind改变的是func的this指向,之后无论如何调用都不会改变
//练习题五
var length = 10
function fn () {
    console.log(this.length)
}
 
var obj = {
    length: 5,
    method: function (fn) {
        fn()
        arguments[0]()
    }
}
obj.method(fn, 1)

//练习题五答案
var length = 10
function fn () {
    console.log(this.length)
}
 
var obj = {
    length: 5,
    method: function (fn) {
        fn()//10
        arguments[0]()//2
        //相当于arguments调用arguments取出第一个值fn(类似obj.属性调用,对象也可以根据[]的方式取出)
    }
}
obj.method(fn, 1)

总结

大家记住一点就行普通函数的this是谁调用就是谁,而箭头函数没有this只能继承来

最后再送一道其他题换换心情

var a = 0
switch (a++) {
	case 0: ++a
	case 1: ++a
	case 5: ++a
}
console.log(a)

这一题的答案就不公布了,大家可以注意一下我写的这个switch是否规范(是否少点东西)就知道答案了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值