JavaScript中的this

在实际应用中,了解 this 的行为是非常重要的,特别是在编写库或框架时,或者当你需要在回调函数中访问特定的上下文时,通常推荐使用箭头函数或者其他方法来确保 this 的正确指向。

在ES6中,this 的值取决于它是如何被调用的。

this 不是一个函数或对象,而是一个特殊的关键字,其值在函数被调用时确定。

以下是在不同场景中 this 的值的概述:

01 全局作用域中的this

在JavaScript中,全局作用域指的是在代码的任何位置都可以访问的、变量和函数的范围。

具体可以这么理解:当你在脚本的顶层(不在任何函数或代码块内部)声明一个变量或函数时,它就在全局作用域中。

在全局作用域中,this 指向全局对象。

在浏览器环境中,全局对象是 window

在 Node.js 环境中,全局对象是 global

下面是一个在浏览器环境中演示这一点的简单例子:

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        "use strict"
        // 在全局作用域中
        console.log(this === window); // 应该输出 true,表示 this 指向 window 对象
        console.log(this); // 输出window对象
        // 定义一个全局变量
        var globalVar = "I am a global variable";

        // 使用 this 访问全局变量
        console.log(this.globalVar); // 输出 "I am a global variable"
        // 使用 window 访问全局变量
        console.log(window.globalVar); // 输出 "I am a global variable"

        // 定义一个函数,并在全局作用域中调用它
        function testFunction() {
            console.log(this); // 在浏览器环境中,输出 window 对象,"use strict"下输出undefined
        }
        testFunction();
    </script>
</body>
</html>

在这个例子中,this 在全局作用域中指向 window 对象。我们通过比较 thiswindow 来验证这一点,并且使用 thiswindow 来访问一个全局变量 globalVar,以证明它们都可以用来访问全局作用域中的变量。

如果你在 Node.js 环境中运行代码,全局对象将是 global,你可以类似地测试:

javascript 复制代码
// 在 Node.js 的全局作用域中
console.log(this === global); // 应该输出 true,表示 this 指向 global 对象

// 定义一个全局变量
global.globalVar = "I am a global variable";

// 使用 this 访问全局变量
console.log(this.globalVar); // 输出 "I am a global variable"

// 使用 global 访问全局变量
console.log(global.globalVar); // 输出 "I am a global variable"

// 定义一个函数,并在全局作用域中调用它
function testFunction() {
  console.log(this); // 在 Node.js 环境中,输出 global 对象
}

testFunction();

在这个 Node.js 例子中,this 在全局作用域中指向 global 对象,并且我们同样使用 thisglobal 来访问一个全局变量 globalVar

02 函数调用中的this

当一个函数不是作为对象的方法调用时(也就是说,它是独立调用的,或者作为回调函数等被调用),this 的值在非严格模式下默认为全局对象(在浏览器中通常是 window),而在严格模式下它是 undefined

下面我将给出两个示例程序来演示这个行为。

首先是非严格模式下的示例:

javascript 复制代码
// 非严格模式
function exampleFunction() {
  console.log(this); // 在非严格模式下,this 指向 window 对象
  console.log(this.globalVar); // => I am a global variable
}

exampleFunction();

// 你可以通过检查 window 对象来验证这一点
function setGlobalVar() {
  window.globalVar = "I am a global variable";
}
setGlobalVar();
exampleFunction(); // 输出包含 globalVar 的 window 对象

在这个例子中,exampleFunction 不是作为任何对象下的方法调用的,因此在非严格模式下,this 指向 window 对象。

我们在 setGlobalVar 函数中设置了一个全局变量 globalVar,然后在 exampleFunction 中通过 this 访问它,证明了 this 确实指向 window

03 对象方法中的this

当一个函数作为对象的方法被调用时,this 关键字指向调用该方法的对象。下面是一个简单的示例来展示这个行为:

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>03-对象方法中的this</title>
</head>
<body>
    <script>
        // 定义一个对象,它有一个方法叫做 greet
        const person = {
            name: "Alice",
            getName: function () {
                console.log(this.name);
            }
        };
        // 调用 person 对象的 getName方法
        person.getName(); // 输出 "Alice"
        // getName中this指向person对象,因为getName是作为 person 的方法被调用的
    </script>
</body>
</html>

在这个例子中,getName 函数是 person 对象的一个方法。当我们通过 person.getName() 调用这个方法时,this 关键字在函数内部指向了 person 对象。因此,this.name 访问的是 person 对象的 name 属性,并输出了相应的信息。

04构造函数中的this

当一个函数被用作构造函数,并使用 new 关键字调用时,this 关键字会指向新创建的对象实例。下面是一个简单的例子来说明这个行为:

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>04-构造函数中的this</title>
</head>
<body>
    <script>
        // 定义一个构造函数  
        function Person(name, age) {
            this.name = name;
            this.age = age;
            // 可以添加一个方法来访问实例属性  
            this.greet = function () {
                console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
            };
        }

        // 使用 new 关键字来调用构造函数,创建一个新的对象实例  
        const alice = new Person('Alice', 25);
        alice.greet(); // 输出 "Hello, my name is Alice and I'm 25 years old."  
        // 验证 this 指向的是 alice 对象  
        console.log(alice.name); // 输出 "Alice"  
        console.log(alice.age);  // 输出 25  

        // 创建一个新的对象实例  
        const bob = new Person('Bob', 30);
        bob.greet(); // 输出 "Hello, my name is Bob and I'm 30 years old."  
        // 验证 this 指向的是 bob 对象  
        console.log(bob.name); // 输出 "Bob"  
        console.log(bob.age);  // 输出 30
    </script>
</body>
</html>

每次使用 new 关键字调用 Person 构造函数时,都会创建一个新的对象实例,并且 this 都会指向那个新对象。这就是为什么 alicebob 有各自独立的 nameage 属性,以及它们各自的 greet 方法。

在这个例子中,Person 函数被用作构造函数,因为我们使用 new 关键字来调用它。当构造函数被调用时,JavaScript 会创建一个新的空对象,并将这个新对象的内部链接([[Prototype]])设置为构造函数的 prototype 对象。然后,构造函数中的代码执行,其中 this 关键字引用新创建的对象。

因此,当我们设置 this.namethis.age 时,我们实际上是在新创建的对象上设置属性。同样,this.greet 方法也是添加到新对象上的一个方法。

05箭头函数中的this

箭头函数在 JavaScript 中是一个非常重要的特性,它提供了一种更简洁的函数书写方式,并且它不绑定自己的 this,而是继承自包围它的函数或全局作用域的 this

这意味着在箭头函数内部,this 的值是在定义箭头函数时确定的,而不是在调用时确定的。

下面是一个简单的示例来说明这个行为:

javascript 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>05-箭头函数中的this</title>
</head>
<body>
    <script>
        function OuterFunction() {
            this.value = 42;
            // 这是一个普通函数  
            function innerFunction() {
                console.log(this.value); // 这里的 this 指向 OuterFunction 的实例  
            }

            // 这是一个箭头函数  
            const arrowFunction = () => {
                console.log(this.value); // 这里的 this 继承自 OuterFunction 的实例  
            };

            // 调用普通函数和箭头函数  
            innerFunction(); // 输出 42  
            arrowFunction(); // 输出 42  
        }

        const obj = new OuterFunction();

        // 当我们在外部调用 innerFunction 时,this 指向全局对象(在浏览器中通常是 window)  
        // 因此,以下代码会抛出一个错误,因为 this.value 是 undefined  
        obj.innerFunction(); // Uncaught TypeError: Cannot read property 'value' of undefined  

        // 但是,箭头函数不会改变它的 this 上下文  
        // 因此,即使我们在外部调用 arrowFunction,它仍然可以访问 OuterFunction 的 this 上下文  
        obj.arrowFunction(); // 输出 42
    </script>
</body>
</html>

OuterFunction 中定义了一个普通函数 innerFunction 和一个箭头函数 arrowFunction

OuterFunction 被调用时,它创建了一个新的对象实例,并且 thisinnerFunctionarrowFunction 中都指向这个新创建的对象。

当我们直接调用 obj.innerFunction() 时,this 指向了全局对象(在浏览器中是 window),因此 this.valueundefined,导致抛出一个错误。

然而,当我们调用 obj.arrowFunction() 时,即使我们是在外部调用的,arrowFunction 内部的 this 仍然指向 OuterFunction 的实例,因此可以正确地访问 value 属性。这是因为箭头函数不绑定自己的 this,而是从定义它的上下文中继承 this

TODO 嵌套函数

全局函数中的嵌套函数

对象方法中的嵌套函数

总结

场景 this 的值 描述
全局作用域 window (浏览器) 或 global (Node.js) 在全局作用域中,this 指向全局对象。
函数调用 undefined (严格模式) 或 window (非严格模式) 当一个函数不是作为对象的方法调用时,this 的值在非严格模式下是 window,在严格模式下是 undefined
对象方法 调用该方法的对象 当一个函数作为对象的方法被调用时,this 指向调用该方法的对象。
构造函数 新创建的对象 当一个函数作为构造函数使用 new 关键字调用时,this 指向新创建的对象。
箭头函数 定义时的上下文 箭头函数不绑定自己的 this,它继承自包围它的函数或全局作用域的 this
事件处理器 调用事件处理器的对象 在DOM事件处理器中,this 通常指向触发事件的元素。
定时器函数 undefined (严格模式) 或 window (非严格模式) setTimeoutsetInterval 的回调函数中,this 的值在非严格模式下是 window,在严格模式下是 undefined
Call, Apply, Bind 指定的对象 使用 callapplybind 方法可以显式地设置 this 的值。

请注意,箭头函数的行为略有不同,因为它们不绑定自己的 this。相反,它们从定义它们的上下文继承 this

希望这个表格能帮助你理解ES6中 this 的不同行为!

在JavaScript中,特别是在ES6及其之后的版本中,回调函数中使用普通函数和箭头函数时,this 的值可能会有所不同。这主要取决于回调函数的调用方式和上下文。以下是普通函数和箭头函数在回调中作为方法使用时 this 的区别:

普通函数

当回调函数是一个普通函数时,this 的值通常取决于该函数如何被调用。如果该函数是作为对象的方法被调用,那么 this 将指向调用该方法的对象。如果该函数是作为回调函数被调用,并且没有使用 callapplybind 方法来显式地设置 this 的值,那么 this 可能会指向全局对象(在浏览器中是 window),或者在没有严格模式的情况下是 undefined

例如:

javascript 复制代码
function Example() {
  this.value = 5;
  
  setTimeout(function() {
    console.log(this.value); // this 指向全局对象或undefined(严格模式)
  }, 1000);
}

const example = new Example(); // 输出可能是 undefined,取决于环境

箭头函数

箭头函数不绑定自己的 this,它继承自包围它的函数或全局作用域的 this。这意味着在箭头函数内部,this 的值将始终与包围它的外部函数的 this 保持一致。

因此,当回调函数是箭头函数时,this 将保持外部函数的 this 值,即使回调函数以不同的方式被调用。

例如:

javascript 复制代码
function Example() {
  this.value = 5;
  
  setTimeout(() => {
    console.log(this.value); // this 继承自Example函数的this,所以输出5
  }, 1000);
}

const example = new Example(); // 输出 5

在这个例子中,即使 setTimeout 是一个全局函数,并且通常会导致回调函数中的 this 指向全局对象,但由于使用了箭头函数,this 仍然保持了 Example 函数的上下文。

总结:在回调函数中,普通函数的 this 值可能会根据回调函数的调用方式而改变,而箭头函数的 this 值则始终与其外部函数的 this 保持一致。因此,在需要确保 this 的值始终为特定上下文时,使用箭头函数通常是更安全的选择。

this不是常量, 它在程序中的不同地方会求值为不同的值。

this是面向对象编程中使用的关键字。在方法体中,this求值为调用方法的对象。

this标准函数箭头函数 中有不同的行为。

1、标准函数中的 this

javascript 复制代码
window.color = 'red';  
// 全局上下文中调用函数时,this 指向 windows
function sayColor() { 
  console.log(this.color); 
} 
sayColor();    // 'red' 
 
let o = { 
 color: 'blue' 
}; 
o.sayColor = sayColor; 
o.sayColor();  // 'blue' 

2、嵌套函数

嵌套函数普通函数内部的this是全局对象,嵌套箭头函数的this是外层对象的this

注意:vue中相反:vue中嵌套普通函数内部的this是vue,而嵌套箭头函数的this是全局对象

javascript 复制代码
 let o = {
    m:function(){
        let self = this;
        this === o;
        console.log('o.m() this:',this);
        f();            
        function f(){
            this === o;
            self ===o;
            console.log('o.m.f() this:',this);
        }
        
        const f2 = ()=>{
            console.log('o.m.f2() this:',this);
        }
        f2();

        const f3 = (function(){
            console.log('o.m.f3() this:',this);
        }).bind(this);
        
        f3();
    }
}
o.m();
console.log(o);

2、箭头函数中的this

javascript 复制代码
window.color = 'red';  
let sayColor = () => console.log(this.color); 
sayColor();    // 'red' 
 
let o = { 
 color: 'blue' 
}; 
o.sayColor = sayColor; 
o.sayColor();  // 'red' 
javascript 复制代码
相关推荐
清灵xmf2 分钟前
揭开 Vue 3 中大量使用 ref 的隐藏危机
前端·javascript·vue.js·ref
ljp_nan6 分钟前
QT --- 初识QT
开发语言·qt
2301_8010741510 分钟前
TypeScript异常处理
前端·javascript·typescript
ᅠᅠᅠ@11 分钟前
异常枚举;
开发语言·javascript·ecmascript
caperxi13 分钟前
前端开发中的防抖与节流
前端·javascript·html
学习路上的小刘16 分钟前
vue h5 蓝牙连接 webBluetooth API
前端·javascript·vue.js
&白帝&16 分钟前
vue3常用的组件间通信
前端·javascript·vue.js
编程版小新17 分钟前
C++初阶:STL详解(四)——vector迭代器失效问题
开发语言·c++·迭代器·vector·迭代器失效
c4fx37 分钟前
Delphi5利用DLL实现窗体的重用
开发语言·delphi·dll
罗_三金37 分钟前
前端框架对比和选择?
javascript·前端框架·vue·react·angular