前言
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是否规范(是否少点东西)就知道答案了