LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

我被 JS 的 this 整不会了:同一个函数,换个“地区”调用,结果就不一样

zhenglin
2026年7月9日 14:51 本文热度 76

写在前面:今天学了一个让我"裂开"的知识点——JS 的 this。老师说了一句话让我记忆深刻:"this 不是声明时决定的,是运行时决定的。" 也就是说,同一个函数,你站着调用、坐着调用、拿锤子敲着调用——结果可能全不一样。听完这节课我才明白,为什么老 JS 程序员看到 this 都要先深呼吸一下。


先来个"剧透":this 到底是谁?

老师用一句话总结了 this 的指向规则:

"this 指向函数的调用者。"

就这么简单?不,没那么简单。因为"函数的调用者"在不同的场景下,是不同的人。

先记住这张表,后面我们一个一个拆开讲:


调用方式this 指向谁举个例子
普通函数调用全局 window(严格模式是 undefinedfn()
作为对象的方法调用该方法的对象obj.say()thisobj
作为构造函数新创建的实例对象new Person()this 是实例
事件处理函数触发事件的元素btn.clickthisbtn
call / apply手动指定的第一个参数fn.call(obj)thisobj
bind绑定时指定的对象const f = fn.bind(obj)
箭头函数没有自己的 this,继承外层的() => {}



一、"use strict":严格模式的"紧箍咒"

1.1 为什么要用严格模式?

看代码第一行:

"use strict";

老师说:"启用严格模式"。这是写在文件最上面的一行字符串,但它会让 JS 的解释器进入"严格模式"。

严格模式干了什么?

最重要的是这一条:普通函数里的 this 不再指向全局 window,而是变成 undefined

"use strict";

function test() {

    console.log(this); // undefined(严格模式下)

}

test(); // 普通函数调用

为什么要这样?因为在非严格模式下,如果你不小心在函数里用了 this,它偷偷指向 window,你可能会意外地创建或修改全局变量——这是灾难性的 bug。

老师说:

 

非严格模式(默认):

function fn() {

    console.log(this); // window(全局对象)

}

严格模式:

"use strict";

function fn() {

    console.log(this); // undefined

}

这就像把宿舍楼的大门锁上了——你没法随意进出(访问全局对象),更安全。


二、表单提交与事件:一场"默认行为"的阻击战

2.1 表单为什么要用 JS 提交?

先看 HTML:

<form class="add-items">

    <input type="text" id="item" placeholder="Add a new tapas" required>

    <input type="submit" value="+ Add Item">

</form>

老师说:

 

表单的默认提交行为:把你的输入发到服务器,然后刷新整个页面。 这在以前是网页交互的主流方式,但现在我们追求的是"无刷新体验"。


2.2 如何阻止默认行为?

function addItem(e) {

    console.log(e);

    // 阻止默认提交行为——刷新页面

    e.preventDefault();

}


oForm.addEventListener('submit', addItem);

e.preventDefault() 就是"阻止默认行为"的方法。

  • 默认行为:表单提交 → 刷新页面。


  • 你做的事情:绑定了事件监听 → e.preventDefault() → 表单不提交。


这就像你在网页上点了一个链接,正常情况下它会跳转。但如果你 e.preventDefault() 了,它就老老实实待在原地,你可以在 JS 里用 fetch 把数据默默发到后台,用户完全感知不到。




三、this 的五大场景:同一个世界,不同的 this

3.1 场景一:普通函数调用 → this 指向 window

"use strict";

var name = "王五";

let obj = {

    name: "张三",

    say: function() {

        console.log(`我是${this.name}`);

    }

};

const fn = obj.say; // 引用式赋值

fn(); // 普通函数被调用,this指向全局


这里的关键:fn = obj.say 之后,fn 只是一个普通函数。你调用 fn(),就是普通函数调用,this 指向全局——在严格模式下是 undefined

这就像你从图书馆借了一本书(obj.say),带回家看。书的内容没变,但环境变了——在图书馆里它是"图书馆的书",在你家里它就是"你桌上的书"。


3.2 场景二:作为对象的方法调用 → this 指向调用对象

let obj = {

    name: "张三",

    say: function() {

        console.log(`我是${this.name}`);

    }

};

obj.say(); // 我是张三 —— this 指向 obj

obj.say()——谁调用的?obj。所以在 say 里面,this 就是 obj

老师说:

"函数作为对象的方法调用,this 指向调用对象。"


3.3 场景三:事件处理函数 → this 指向触发事件的元素

document

    .querySelector('.lnk')

    .addEventListener('click', goBaidu);


function goBaidu(e) {

    console.log(this); // 指向触发事件的元素——<a> 标签

    e.preventDefault();

}

事件处理函数里的 this,指向触发事件的 DOM 元素。 点击了 <a> 标签,this 就是那个 <a>

但这里有个坑——事件处理函数是需要异步执行的。 等到用户点击时,函数才被执行。

老师说:

"事件一定要有一个事件处理函数this 默认指向事件触发对象。如果我想手动指定 this?call、apply、bind 都可以。call、apply 立即运行,但是我们要的函数是异步运行的,所以用 bind。"


3.4 场景四:手动指定 this——call、apply、bind

call:立即执行,一个一个传参


obj.say.call(obj2); // 借用 obj 的 say 方法,this 指向 obj2

obj.speak.call(obj2, '你好', '我是李四'); // 参数一个一个传

apply:立即执行,参数传数组

obj.say.apply(obj2, ['你好', '我是李四']);

// 效果和 call 一样,但参数放在数组里

bind:不立即执行,返回一个新函数

const addItemBind = addItem.bind(obj2); // 全新函数,不立即执行

addItemBind(); // 调用时,this 指向 obj2

老师说:

"call、apply 都可以手动指定 this,区别是 call 是一个一个传参数,apply 是传数组对象。bind 手动指定 this,返回一个新的函数,但不立即执行。"

用一张图记住三者的区别:

 


3.5 场景五:箭头函数——没有 this 的"另类"

看这段有坑的代码:

var name = "梅西";

let obj = {

    name: "姆巴佩",

    say: function() {

        console.log(this.name); // 姆巴佩(this 指向 obj)

        setTimeout(function() {

            console.log(this.name); // 梅西(this 指向 window!)

        }, 1000);

    }

};

obj.say();

这里的 setTimeout 里的回调函数是普通函数,它的 this 指向全局 window。 所以 this.name 是全局的 var name = "梅西",而不是 obj.name = "姆巴佩"

怎么解决?用箭头函数

let obj = {

    name: "姆巴佩",

    say: function() {

        console.log(this.name); // 姆巴佩

        setTimeout(() => {

            console.log(this.name); // 姆巴佩(箭头函数没有自己的 this!)

        }, 1000);

    }

};

obj.say();

老师说:

"箭头函数没有 this 指向,是简化的函数。"

箭头函数不创建自己的 this,它会从外层作用域继承 this。所以箭头函数里的 this,和它外面那个函数的 this 是一样的。




四、var 和 let 的区别:为什么 var 会污染 window?

var name = "王五";  // 挂载到 window 上

let name2 = "赵六"; // 不会挂载到 window 上


console.log(window.name);  // "王五"

console.log(window.name2); // undefined

老师说:

"var 声明的变量,挂载在 window 上,污染了 window 对象。let 就不会污染 window 对象。"

这就是为什么现在都用 letconst——它们不会污染全局对象。




五、完整代码串讲:把一切串起来

common.js 里的完整代码:

"use strict";

const oForm = document.querySelector('.add-items');


let obj2 = { name: "李四" };

let obj = {

    name: "张三",

    say: function() {

        console.log(`我是${this.name}`);

    },

    speak: function(a, b) {

        console.log(a, b);

        console.log(this);

    }

};


// bind:手动指定 this,返回新函数,不立即执行

const addItemBind = addItem.bind(obj2);

oForm.addEventListener('submit', addItemBind); // 事件触发时,this 指向 obj2


function addItem(e) {

    e.preventDefault(); // 阻止默认提交

}


// 事件处理函数中的 this 指向触发事件的元素

document.querySelector('.lnk')

    .addEventListener('click', goBaidu);


function goBaidu(e) {

    console.log(this); // <a> 标签

    e.preventDefault();

}


// 作为对象的方法调用 — this 指向 obj

obj.say(); // 我是张三


// 引用式赋值 — this 指向全局(严格模式下 undefined)

const fn = obj.say;

fn();


// call 手动指定 this

obj.say.call(obj2); // 我是李四


// apply 传数组

obj.speak.call(obj2, '你好', '我是李四');

obj.speak.apply(obj2, ['你好', '我是李四']);


// bind 返回新函数

const fn2 = obj.speak.bind(obj2);

fn2('你好', '我是李四');


六、总结:this 的"血泪史"

 

最核心的一句话: this 是在函数运行时决定的,不是在声明时决定的。同样的函数,用不同的方式调用,this 指向完全不一样。


写在最后

今天终于把 JS 的 this 搞懂了。以前看到 this 就头皮发麻,现在知道了——不要管函数在哪定义的,只看它怎么被调用的。


假设有人问你:"callapplybind 有什么区别?"

你可以淡定地说:

"callapply 都是立即执行函数,手动指定 this。区别是 call 一个一个传参数,apply 传数组。bind 不立即执行,它返回一个新函数,新函数的 this 被永久绑定到指定的对象上。bind 特别适合事件处理函数这种异步场景——先用 bind 绑好 this,等事件触发时再执行。"


阅读原文


该文章在 2026/7/9 14:51:02 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-9  粤公网安备44030602007207号