【超简单】一文搞懂this指向的问题

箭头函数和普通函数的区别

1、箭头函数的this指向是静态的

this始终指向函数声明时所在作用域下的this的值,如果当前作用域下没有this,沿着作用域链往上寻找

var name = 'window'
const obj = {
    name: 'cxk',
    getName1: function() {
        console.log(this.name)
    },
    getName2: () => {
        console.log(this.name) // 定义时,当前所在作用域并没有this,沿着作用域链找到window
    }
}

obj.getName1()  // 'cxk'
obj.getName2()  // 'window'

2、不能作为构造实例化对象

let Person = (name, age) => {
    this.name = name;
    this.age = age;
    console.log(this)
}
Person()
let my = new Person('xie', 18);  //创建不了实例化对象
console.log(my);  //报错  这个this一直是指向Person的,因此创建不了

3、不能使用arguments 变量

let fn = () => {
    console.log(arguments);
}
fn(1, 2, 3, 4);  //报错

this指向问题

1、默认绑定

以全局作用域或者普通函数直接调用,this永远是window(注意定时器里面的this指向window)

var btn = document.getElementsByTagName('button')[0]
function fn() {
	console.log(this); // Window

}
// window.fn();
fn();

// window.setTimeout(function() {
// 	console.log(this); // Window
// }, 1000);

//定时器前面省略了window
setTimeout(function() {
	console.log(this);  // Window
}, 1000);

// 这里用了隐式绑定,写在这里只是为了说明直接使用定时器其实本质就是直接全局调用
btn.onclick = function() {
    //谁调用的方法,this就指向谁
	console.log(this) // btn
	setTimeout(function() {
		console.log(this); // Window
	}, 1000)
    // 这里是箭头函数,this是静态是,为其函数声明时,所在作用域下的this指向
    setTimeout(() => {
        console.log(this) // btn
    }, 1000);
}

2、隐式绑定

以方法调用的时候,谁调用的this就指向谁

案例一:

function foo() {
    console.log(this) // { name: 'cxk', foo: foo(){} }
}
var obj = {
    name: 'cxk',
    foo: foo
}
obj.foo()

案例二:

var obj = {
    name: 'cxk',
    age: 16,
    getAge: function () {
        console.log('哥哥的年龄:', this.age) // 哥哥的年龄: undefined
    }
}
var fn = obj.getAge
fn()

为什么呢?

因为this的绑定和定义的位置(编写的位置)没有关系,this的绑定和调用方式以及调用的位置有关系
this是在运行时被绑定的,不管你在哪里定义的,反正我在调用的时候是独立的,所以this是window。

案例三:

var obj1 = {
    name: 'obj1',
    foo: function() {
        console.log(this)
    }
}

var obj2 = {
    name: 'obj2',
    // 这里只是把obj1的函数赋值给obj2,并没有调用
    bar: obj1.foo
}
// 函数是obj2调用的,所以是obj2
obj2.bar()  // {name: 'obj2', bar: function() {}}

3、显式绑定

使用call和apply调用的时候,this是指定的那个对象

var obj = {
    name: "obj"
}
function foo() {
    console.log('函数被调用了', this)
}
foo() // Window
foo.call(obj) // {name: obj}
foo.call('call') // 'call'
foo.apply('apply') // 'apply'
var newFoo = foo.bind('aa')
newFoo() // 'aa'

4、new绑定

以构造函数调用的时候,this指向的是那个实例对象

function Fun() {
    console.log(this) // this 指向的是fun实例对象
}
var fun = new Fun()

5、其他补充

5.1、显示绑定的优先级高于隐式绑定
function foo() {
	console.log(this)
}
var obj = {
	name: "obj",
	foo: foo.bind('aaa')
}
obj.foo()   // 'aaa'
5.2、new的优先级高于隐式绑定
var obj = {
	name: 'obj',
	foo: function() {
		console.log(this)
	}
}
var f = new obj.foo() // foo {}
5.3、new关键字不能和appl和call一起使用
function foo() {
     console.log(this)
}
// // 特殊情况
foo.call(null)  // Window
foo.call(undefined)  // Window

面试题:

例子1:

var name = "222";
var a = {
    name : "111",
    say : function () {
        console.log(this.name);
    }
}
var fun = a.say;
fun();  // 空执行,没人调用 ,在全局调用的window.name   222  ,
a.say();  // a调用 111
var b = {
    name : "333",
    say : function (fun) {
        fun();
    }
}
b.say(a.say);  //这里没调用fun(),空执行  222

例子2:

var name = "window"
var person = {
    name: "person",
    sayName: function () {
        console.log(this.name)
    }
}
function sayName() {
    var sss = person.sayName
    sss() // 相当于直接调用 'w'
    person.sayName(); // 隐式调用 'p'
    (person.sayName)(); // 隐式调用 'p'
    (b = person.sayName)() // 直接调用 'w'
}
sayName()

例子3:

var name = "window"
var person1 = {
    name: "person1",
    foo1: function() {
        console.log(this.name)
    },
    foo2: () => console.log(this.name),
    foo3: function() {
        return function() {
            console.log(this.name)
        }
    },
    foo4: function() {
        return () => {
            console.log(this.name)
        }
    },
}
var person2 = {name: 'person2'}

// 以对象的方法直接调用
person1.foo1() // 隐式调用:p1

// 通过call将this指向person2
person1.foo1.call(person2) // 显式调用: p2

// this的指向是静态的,为其声明时所在的作用域的this指向
person1.foo2() // 箭头函数:w

// this的指向是静态的,为其声明时所在的作用域的this指向
person1.foo2.call(person2) // 箭头函数: w

// 隐式调用后返回一个函数再调用,此时这个函数相当于直接调用
person1.foo3()() // 直接调用: w

// 通过call显式调用改变this指向后返回一个函数再调用,此时这个函数相当于直接调用
person1.foo3.call(person2)() // 直接调用: w

// 隐式调用后返回一个函数再调用,调用的同时通过call显式调用改变this的指向
person1.foo3().call(person2) // 显式调用: p2

// this的指向是静态的,为其声明时所在的作用域的this指向,此函数声明时上级是person1
person1.foo4()() // 箭头函数: p1

// this的指向是静态的,为其声明时所在的作用域的this指向,声明前上级已被显式改为person2
person1.foo4.call(person2)() // 箭头函数: p2

// this的指向是静态的,为其声明时所在的作用域的this指向
person1.foo4().call(person2) // 箭头函数: p1

总结:

普通函数:

  • 以函数形式直接调用的时候,this永远是window
  • 以方法调用的时候,谁调用的this就指向谁
  • 以构造函数调用的时候,this指向的是那个实例对象
  • 使用call和apply调用的时候,this是指定的那个对象

箭头函数:

  • this是静态的,this始终指向函数声明时所在作用域下的 this的值,如果当前作用域下没有this,沿着作用域链往上寻找

原文链接:https://juejin.cn/post/7356041157993611276 作者:吃辣别喊我

(0)
上一篇 2024年4月11日 上午10:42
下一篇 2024年4月11日 上午10:53

相关推荐

发表回复

登录后才能评论