4大场景带你理清javascript中this的指向
在日常的Javascript开发中,我们会经常使用到this,但是很多初学者很容易混淆this是什么,为什么自己的代码 不能正常的运行?
那么,到底是谁this
?🤔
从技术上讲,JavaScript
this
是一个关键字,指的是函数所属的**对象。**this 的值取决于函数的调用方式,即所谓的运行时绑定。
用最简单的一句话来概括:this指向函数调用的所有者,请注意这里是调用者,并不是函数本身。同一个函数在不同的场景中,可能会有不同的调用者。所以说,要想判断当前的this指向,只要找到确切的调用者,就很容易确定this代表谁!
在日常的使用中,我们大概会面对下面四种场景!
1. 默认绑定/直接调用
在默认绑定(直接调用函数)中,this
指向全局对象。全局对象也就是window
对象。默认绑定适用于不属于任何嵌套函数/对象的独立函数。
function myFunction() {
console.log(this)
}
myFunction(); // Window {}
2. 隐式绑定/方法调用
使用点符号来调用对象中的方法时,会隐式绑定this为当前对象,也就是点符号左边的对象。
function myFunction() {
console.log(this)
}
const obj = {
someKey: 1,
myFunction: myFunction,
}
obj.myFunction();
// {someKey: 1, myFunction: ƒ}. ie. obj
嵌套函数
当函数定义且调用在对象方法的内部时,此时函数的上下文仅取决于调用类型。而不取决于外部函数的上下文。也就是说此时的函数的this任然取决于内部函数在父函数内部的调用方式。
const obj = {
someKey: 1,
outer: function() {
function inner(){
console.log(this);
}
inner();
},
}
obj.outer(); // Window {}
在此示例中,即使outer
是使用隐式绑定inner
调用的,也是使用默认绑定调用的。我们已经知道默认绑定是如何工作的。因此, this
指向 Window 对象。
方法与对象分离
当我们将对象方法复制到新变量时,我们正在创建对函数的引用。这意味着,该方法处理的新变量将直接指向该方法,而不是包含它的父对象。
function myFunction() {
console.log(this);
}
const obj = {
someKey: 1,
myFunction: myFunction,
}
const newFunction = obj.myFunction;
newFunction(); // Window {}
在本例中,newFunction 直接引用 myFunction。所以它会打印window对象,因为它间接地变成了默认绑定。
3.显式绑定/间接调用
这种场景,主要是使用call()、apply() 和 bind()强制改变函数内部的this指向。
**call():**在函数调用过程中传入需要的对象作为第一个参数。实际参数在对象之后传递。
**apply():**与 call() 类似,但传递实际参数的方式有所不同。在这里,实际参数作为数组传递。
function myFunction(param1, param2) {
console.log(this)
}
const obj = {
someKey: 1,
}
const param1 = 1, param2 = 2;
myFunction.call(obj, param1, param2) // {someKey: 1}
myFunction.apply(obj, [param1, param2]) // {someKey: 1}
bind():使用此方法创建一个具有固定this
. 使用 bind() 创建的这些类型的函数通常称为绑定函数。
function myFunction() {
console.log(this)
}
const obj = {
someKey: 1,
}
const boundFunction = myFunction.bind(obj)
boundFunction(); // {someKey: 1}
4.new/构造函数调用
没有返回的函数
当我们使用 new 操作符调用一个函数时,内部会完成以下步骤:
- 调用构造函数并在内部创建一个对象,继承使用的构造函数的原型。
- 根据函数定义将属性和函数添加到此对象。
- 这个新创建的对象被返回并在函数调用时分配给 LHS 变量。
function myFunction(){
this.someKey = 1;
this.inner = function(){
console.log(this);
}
// JS internally returns the object
}
const obj = new myFunction();
obj.inner() // {someKey: 1, inner: ƒ} with myFunction prototype
带返回的函数
返回的对象在函数调用时分配给 LHS 变量,并且构造函数的原型不是继承的。
function myFunction(){
return {
someKey: 1,
}
}
const obj = new myFunction();
console.log(obj); // {someKey: 1} without myFunction prototype
箭头函数
JS 中的普通函数遵守上面提到的 4 条规则。但是 ES6 引入了一种不使用这些规则的特殊函数,称为箭头函数。
箭头函数使用“词法范围”来确定
this
应该是什么值。词法作用域是一种奇特的说法,它使用定义它的外部函数中的“this”。
简单地说,当一个箭头函数被调用时,JS 字面意思是this
从声明箭头函数的外部函数中获取值。我再说一遍,外部函数,而不是定义它的外部对象。
- 如果外部函数是普通函数,这取决于外部函数的绑定类型。
- 如果外层函数是箭头函数,JS 再次检查下一个外层函数,这个过程一直持续到全局对象。
function outer(){
let inner = () => {
console.log(this);
};
inner()
}
const objA = {
someKey: 1,
outer : outer,
};
const objB = {
someKey: 2,
}
outer(); // Window {}
objA.outer(); // {someKey: 1, outer: ƒ} --> objA
outer.call(objB) // {someKey: 2} --> objB
注意:没有任何绑定规则对箭头函数有任何直接影响
const myFunction = () => {
console.log(this);
}
const objA = {
myFunction: myFunction,
inner: () => {
console.log(this);
}
}
const objB = {}
myFunction(); // Window {}
objA.myFunction() // Window {}
objA.inner() // Window {}
myFunction.call(objB); // Window {}
const objC = new myFunction() // myFunction is not a constructor
- 点赞
- 收藏
- 关注作者
评论(0)