刚仔先森 發表於 2026-1-13 09:38:55

一文详解js中如何改变this指向

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">1.this指向</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">1.1常见的this</a></li></ul><li><a href="#_label1">2. call、apply、bind</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_1">2.1 call</a></li><li><a href="#_lab2_1_2">2.2 apply</a></li><li><a href="#_lab2_1_3">2.3 bind</a></li></ul><li><a href="#_label2">总结</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>1.this指向</h2>
<blockquote><p>什么是this指向?</p></blockquote>
<p>在 JavaScript中,this的指向取决于函数被调用的方式,而非定义的位置</p>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>1.1常见的this</h3>
<ol><li>独立函数<br />函数独立调用时,this指向全局对象(浏览器中为 window,Node.js中为global)。</li></ol>
<div class="jb51code"><pre class="brush:js;">function show() {
console.log(this); // window(非严格模式)
}
show();
</pre></div>
<blockquote><p>严格模式下为undefined</p></blockquote>
<div class="jb51code"><pre class="brush:js;">function show() {
"use strict"
console.log(this); // undefined
}
show(); // 如果window.show()那么此时的this指向的就是window
</pre></div>
<ol start="2"><li>对象函数<br />函数作为对象方法调用时,this 指向调用它的对象</li></ol>
<div class="jb51code"><pre class="brush:js;">const user = {
name: "Alice",
greet() {
    console.log(`Hello, ${this.name}!`); // Hello, Alice!
}
};
user.greet();
</pre></div>
<blockquote><p>方法被分离后调用会导致this丢失</p></blockquote>
<div class="jb51code"><pre class="brush:js;">const func = user.greet;
func(); // ❌ 错误:this 丢失(指向 window/undefined)
</pre></div>
<ol start="3"><li>箭头函数<br />箭头函数的this定义:箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this就继承了定义函数的对象。<br />箭头函数内的this就是箭头函数外的那个this为什么?<br />注意:箭头函数没有自己的this</li></ol>
<div class="jb51code"><pre class="brush:js;">const obj = {
name: "Dave",
regularFunc: function() {
      console.log(this.name); // Dave(隐式绑定)
},
arrowFunc: () =&gt; {
      console.log(this.name); // 空(继承外层 this)window上没有name
}
};
obj.regularFunc();
obj.arrowFunc();
</pre></div>
<div class="jb51code"><pre class="brush:js;">let name = "123";
let person = {
name: "456",
fn1: function() {
      // 这边的this和下面的setTimeout函数下的this相等
      let that = this;
      setTimeout(() =&gt; {
          console.log(this.name, that === this); // '456' true
      }, 0);
},
fn2: function() {
      // 这边的this和下面的setTimeout函数下的this不相等
      let that = this;
      setTimeout(function() {
          console.log(this.name, that === this); // '123' false
      }, 0);
},
};
person.fn1(); // '456' true
person.fn2(); // '123' false
</pre></div>
<ol start="4"><li>DOM节点<br />非严格模式</li></ol>
<div class="jb51code"><pre class="brush:xhtml;">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;title&gt;最编程 创未来&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;button&gt;变色&lt;/button&gt;
    &lt;script&gt;
      let elements = document.getElementsByTagName("button");
      elements.addEventListener(
      "click",
      function () {
          this.style.backgroundColor = "#A5D9F3";
      },
      false
      );
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011309215186.png" /></p>
<p>严格模式</p>
<div class="jb51code"><pre class="brush:xhtml;">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;title&gt;最编程 创未来&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;button&gt;变色&lt;/button&gt;
    &lt;script&gt;
      'use strict'
      var elements = document.getElementsByTagName("button");
      elements.addEventListener(
      "click",
      function () {
          console.log(this)
          this.style.backgroundColor = "#A5D9F3";
      },
      false
      );
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

</pre></div>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202601/2026011309215112.png" /></p>
<p>5. 内联事件函数</p>
<p>当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素,不区分严格或非严格模式</p>
<div class="jb51code"><pre class="brush:xhtml;">&lt;button onclick="console.log(this)"&gt;点击测试&lt;/button&gt; &lt;!-- &lt;button onclick="console.log(this)"&gt;点击测试&lt;/button&gt; --&gt;
</pre></div>
<p>当代码被包括在函数内部执行时,其this指向等同于函数直接调用的情况,即在非严格模式指向全局对象window</p>
<div class="jb51code"><pre class="brush:xhtml;">&lt;button onclick="(function() { console.log(this) })()"&gt;点击测试&lt;/button&gt; &lt;!-- Window {window: Window, self: Window, document: document, name: '最编程', location: Location, …} --&gt;
</pre></div>
<p>当代码被包括在函数内部执行时,其this指向等同于函数直接调用的情况,在严格模式指向undefined</p>
<div class="jb51code"><pre class="brush:xhtml;">&lt;button onclick="(function() {'use strict'; console.log(this) })()"&gt;点击测试&lt;/button&gt; &lt;!-- undefined --&gt;
</pre></div>
<ol start="6"><li>构造函数<br />构造函数中,this 指向新创建的实例对象Person{}</li></ol>
<div class="jb51code"><pre class="brush:js;">function Person(name) {
this.name = name;
}
const charlie = new Person("金小子");
console.log(charlie.name); // 金小子
</pre></div>
<div class="jb51code"><pre class="brush:js;">// 伪代码展示 new 的操作流程
const charlie = new Person("金小子");

// 实际发生的步骤:
// 1. 创建新对象
const tempObj = {};

// 2. 设置原型链
tempObj.__proto__ = Person.prototype;

// 3. 将 this 绑定到新对象并执行构造函数
Person.call(tempObj, "金小子"); // 此时构造函数内的 this = tempObj

// 4. 返回新对象
const charlie = tempObj;
</pre></div>
<p>以上就是常见的this指向,那么接下来来看改变this指向的方式。</p>
<p class="maodian"><a name="_label1"></a></p><h2>2. call、apply、bind</h2>
<blockquote><p>JavaScript 的 call、apply 和 bind 都是用于显式绑定函数执行时的 this 指向。</p></blockquote>
<p>三者核心区别:</p>
<p>● call:立即执行函数,逐个传递参数</p>
<p>● apply:立即执行函数,数组形式传递参数</p>
<p>● bind:不立即执行,返回新函数(永久绑定 this 和部分参数)</p>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>2.1 call</h3>
<p>call</p>
<p>语法</p>
<div class="jb51code"><pre class="brush:js;">func.call(thisArg, arg1, arg2, ...)
</pre></div>
<p>例子</p>
<div class="jb51code"><pre class="brush:js;">function greet(message) {
console.log(`${message}, ${this.name}!`);
}

const person = { name: "Alice" };

// 将 greet 的 this 指向 person,并传递参数
greet.call(person, "Hello"); // 输出: "Hello, Alice!"
</pre></div>
<p>● greet 中的 this 原本指向全局(如 window),但通过 call 将 this 绑定到 person 对象</p>
<p>● &ldquo;Hello&rdquo; 作为参数逐个传递</p>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>2.2 apply</h3>
<p>语法</p>
<div class="jb51code"><pre class="brush:js;">func.apply(thisArg, )
</pre></div>
<p>示例</p>
<div class="jb51code"><pre class="brush:js;">function introduce(age, job) {
console.log(`${this.name} is ${age} years old and works as a ${job}.`);
}

const person = { name: "Bob" };

// 将 introduce 的 this 指向 person,参数通过数组传递
introduce.apply(person, ); // 输出: "Bob is 30 years old and works as a developer."
</pre></div>
<p>● 参数以数组 形式传递(适合动态参数场景)</p>
<p>● 等同于 introduce.call(person, 30, &ldquo;developer&rdquo;)</p>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>2.3 bind</h3>
<p>语法</p>
<div class="jb51code"><pre class="brush:js;">const newFunc = func.bind(thisArg, arg1, arg2, ...)
newFunc()
</pre></div>
<p>示例</p>
<div class="jb51code"><pre class="brush:js;">function logHobby(hobby1, hobby2) {
console.log(`${this.name} likes ${hobby1} and ${hobby2}.`);
}

const person = { name: "Charlie" };

// 创建新函数,永久绑定 this 和部分参数
const boundFunc = logHobby.bind(person, "hiking");

// 调用新函数时只需传入剩余参数
boundFunc("reading"); // 输出: "Charlie likes hiking and reading."
</pre></div>
<p>● bind 返回一个新函数 boundFunc,其 this 永久绑定为 person</p>
<p>● &ldquo;hiking&rdquo; 被预设为第一个参数,调用时只需传递剩余参数</p>
<p class="maodian"><a name="_label2"></a></p><h2>总结</h2>
<table><thead><tr><th>方法</th><th>执行时机</th><th>参数形式</th><th>是否返回新函数</th></tr></thead><tbody><tr><td>call</td><td>立即执行</td><td>逐个参数 (arg1, arg2)</td><td>❌</td></tr><tr><td>apply</td><td>立即执行</td><td>数组 ()</td><td>❌</td></tr><tr><td>bind</td><td>延迟执行</td><td>逐个参数(可部分预设)</td><td>✅</td></tr></tbody></table>
<p>🌟 核心总结</p>
<p>this 指向是 JavaScript 中核心且易混淆的知识点,其本质遵循「调用决定指向」的原则(箭头函数除外):</p>
<p>普通函数:this 指向调用它的对象,独立调用时指向全局(严格模式为 undefined);</p>
<p>箭头函数:无自有 this,继承定义时外层作用域的 this;</p>
<p>构造函数 / 事件处理:this 分别指向实例对象、触发事件的 DOM 元素;</p>
<p>显式绑定:call/apply/bind 可强制修改 this 指向,三者仅在「执行时机、参数形式」上有差异(call/apply 立即执行,bind 返回新函数)。</p>
<p>📌 实践建议</p>
<p>日常开发中,优先通过「对象方法调用」「箭头函数」「bind 绑定」明确 this 指向,避免全局 this 污染;</p>
<p>处理动态参数时用 apply,需预设参数 / 延迟执行时用 bind,简单传参优先 call;</p>
<p>严格模式下需格外注意独立函数的 this 指向(变为 undefined),避免意外报错。</p>
<p>🎯 记忆口诀</p>
<p>this 指向:「谁调用,指向谁;箭头函数,找外层;构造 /new,指实例;显式绑定,听 call/apply/bind」;</p>
<p>绑定三兄弟:「call 逐个传,apply 数组传,bind 绑完等调用」。</p>
頁: [1]
查看完整版本: 一文详解js中如何改变this指向