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

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

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

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

js 复制代码
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、不能作为构造实例化对象

js 复制代码
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 变量

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

this指向问题

1、默认绑定

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

js 复制代码
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就指向谁

案例一:

js 复制代码
function foo() {
    console.log(this) // { name: 'cxk', foo: foo(){} }
}
var obj = {
    name: 'cxk',
    foo: foo
}
obj.foo()

案例二:

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

为什么呢?

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

案例三:

js 复制代码
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是指定的那个对象

js 复制代码
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指向的是那个实例对象

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

5、其他补充

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

面试题:

例子1:

js 复制代码
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:

js 复制代码
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:

js 复制代码
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,沿着作用域链往上寻找
相关推荐
小小小小宇1 小时前
ESLint 插件笔记
前端
纪伊路上盛名在1 小时前
jupyter内核崩溃
前端·数据库·jupyter·生物信息·基因组·k-mer
Net蚂蚁代码2 小时前
Angular入门的环境准备步骤工作
前端·javascript·angular.js
小着5 小时前
vue项目页面最底部出现乱码
前端·javascript·vue.js·前端框架
lichenyang4537 小时前
React ajax中的跨域以及代理服务器
前端·react.js·ajax
呆呆的小草7 小时前
Cesium距离测量、角度测量、面积测量
开发语言·前端·javascript
WHOAMI_老猫8 小时前
xss注入遇到转义,html编码绕过了解一哈
javascript·web安全·渗透测试·xss·漏洞原理
一 乐8 小时前
民宿|基于java的民宿推荐系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·源码
testleaf9 小时前
前端面经整理【1】
前端·面试
好了来看下一题9 小时前
使用 React+Vite+Electron 搭建桌面应用
前端·react.js·electron