热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript的this指向题目深度剖析

JavaScript中的this指向题目有许多博客在诠释,依然有许多人问。上周我们的开辟团队一连两个人碰到相干题目,所以我不得不将关于前端构建手艺的交流会延长了半个时刻议论this

Javascript 中的 this 指向题目有许多博客在诠释,依然有许多人问。上周我们的开辟团队一连两个人碰到相干题目,所以我不得不将关于前端构建手艺的交流会延长了半个时刻议论 this 的题目。

与我们罕见的许多言语差别,Javascript 函数中的 this 指向并不是在函数定义的时刻肯定的,而是在挪用的时刻肯定的。换句话说,函数的挪用体式格局决议了 this 指向

Javascript 中,一般的函数挪用体式格局有三种:直接挪用、要领挪用和 new 挪用。除此之外,另有一些迥殊的挪用体式格局,比方经由过程 bind() 将函数绑定到对象以后再举行挪用、经由过程 call()apply() 举行挪用等。而 es6 引入了箭头函数以后,箭头函数挪用时,其 this 指向又有所差别。下面就来剖析这些状况下的 this 指向。

直接挪用

直接挪用,就是经由过程 函数名(...) 这类体式格局挪用。这时刻,函数内部的 this 指向全局对象,在浏览器中全局对象是 window,在 NodeJs 中全局对象是 global

来看一个例子:

// 简朴兼容浏览器和 NodeJs 的全局对象
const _global = typeof window === "undefined" ? global : window;
function test() {
console.log(this === _global); // true
}
test(); // 直接挪用

这里须要注重的一点是,直接挪用并不是指在全局作用域下举行挪用,在任何作用域下,直接经由过程 函数名(...) 来对函数举行挪用的体式格局,都称为直接挪用。比方下面这个例子也是直接挪用

(function(_global) {
// 经由过程 IIFE 限制作用域
function test() {
console.log(this === _global); // true
}
test(); // 非全局作用域下的直接挪用
})(typeof window === "undefined" ? global : window);

bind() 对直接挪用的影响

另有一点须要注重的是 bind() 的影响。Function.prototype.bind() 的作用是将当前函数与指定的对象绑定,并返回一个新函数,这个新函数不论以什么样的体式格局挪用,其 this 一直指向绑定的对象。照样来看例子:

const obj = {};
function test() {
console.log(this === obj);
}
const testObj = test.bind(obj);
test(); // false
testObj(); // true

那末 bind() 干了啥?无妨模仿一个 bind() 来相识它是怎样做到对 this 发生影响的。

const obj = {};
function test() {
console.log(this === obj);
}
// 自定义的函数,模仿 bind() 对 this 的影响
function myBind(func, target) {
return function() {
return func.apply(target, arguments);
};
}
const testObj = myBind(test, obj);
test(); // false
testObj(); // true

从上面的示例能够看到,起首,经由过程闭包,坚持了 target,即绑定的对象;然后在挪用函数的时刻,对原函数运用了 apply 要领来指定函数的 this。固然原生的 bind() 完成能够会差别,而且更高效。但这个示例说清晰明了 bind() 的可行性。

call 和 apply 对 this 的影响

上面的示例顶用到了 Function.prototype.apply(),与之类似的另有 Function.prototype.call()。这两要领的用法请人人自身经由过程链接去看文档。不过,它们的第一个参数都是指定函数运转时个中的 this 指向。

不过运用 applycall 的时刻依然须要注重,假如目次函数自身是一个绑定了 this 对象的函数,那 applycall 不会像预期那样实行,比方

const obj = {};
function test() {
console.log(this === obj);
}
// 绑定到一个新对象,而不是 obj
const testObj = test.bind({});
test.apply(obj); // true
// 希冀 this 是 obj,即输出 true
// 然则因为 testObj 绑定了不是 obj 的对象,所以会输出 false
testObj.apply(obj); // false

因而可知,bind() 对函数的影响是深远的,慎用!

要领挪用

要领挪用是指经由过程对象来挪用其要领函数,它是 对象.要领函数(...) 如许的挪用情势。这类状况下,函数中的 this 指向挪用该要领的对象。然则,一样须要注重 bind() 的影响。

const obj = {
// 第一种体式格局,定义对象的时刻定义其要领
test() {
console.log(this === obj);
}
};
// 第二种体式格局,对象定义好以后为其附加一个要领(函数表达式)
obj.test2 = function() {
console.log(this === obj);
};
// 第三种体式格局和第二种体式格局道理雷同
// 是对象定义好以后为其附加一个要领(函数定义)
function t() {
console.log(this === obj);
}
obj.test3 = t;
// 这也是为对象附加一个要领函数
// 然则这个函数绑定了一个不是 obj 的别的对象
obj.test4 = (function() {
console.log(this === obj);
}).bind({});
obj.test(); // true
obj.test2(); // true
obj.test3(); // true
// 受 bind() 影响,test4 中的 this 指向不是 obj
obj.test4(); // false

这里须要注重的是,后三种体式格局都是预定定义函数,再将其附加给 obj 对象作为其要领。再次强调,函数内部的 this 指向与定义无关,受挪用体式格局的影响。

要领中 this 指向全局对象的状况

注重这里说的是要领中而不是要领挪用中。要领中的 this 指向全局对象,假如不是因为 bind(),那就肯定是因为不是用的要领挪用体式格局,比方

const obj = {
test() {
console.log(this === obj);
}
};
const t = obj.test;
t(); // false

t 就是 objtest 要领,然则 t() 挪用时,个中的 this 指向了全局。

之所以要迥殊提出这类状况,重要是因为常常将一个对象要领作为回调传递给某个函数以后,却发明运转结果与预期不符——因为疏忽了挪用体式格局对 this 的影响。比方下面的例子是在页面中对某些事变举行封装以后迥殊轻易碰到的题目:

class Handlers {
// 这里 $button 假定是一个指向某个按钮的 jQuery 对象
constructor(data, $button) {
this.data = data;
$button.on("click", this.onButtonClick);
}
onButtonClick(e) {
console.log(this.data);
}
}
const handlers = new Handlers("string data", $("#someButton"));
// 对 #someButton 举行点击操纵以后
// 输出 undefined
// 但预期是输出 string data

this.onButtonClick 作为一个参数传入 on() 以后,事宜触发时,理论上是对这个函数举行的直接挪用,而不是要领挪用,所以个中的 this 会指向全局对象 —— 但实际上因为挪用事宜处置惩罚函数的时刻,this 指向会绑定到触发事宜的 DOM 元素上,所以这里的 this 是指向触发事宜的的 DOM 元素(注重:this 并不是 jQuery 对象),即 $button.get(0)(注重代码前解释中的假定)。

要处理这个题目有许多种要领:

// 这是在 es5 中的处理办法之一
var _this = this;
$button.on("click", function() {
_this.onButtonClick();
});
// 也能够经由过程 bind() 来处理
$button.on("click", this.onButtonClick.bind(this));
// es6 中能够经由过程箭头函数来处置惩罚,在 jQuery 中慎用
$button.on("click", e => this.onButtonClick(e));

不过请注重,将箭头函数用作 jQuery 的回调时形成要警惕函数内对 this 的运用。jQuery 大多数回调函数(非箭头函数)中的 this 都是示意挪用目的,所以能够写 $(this).text() 如许的语句,但 jQuery 没法转变箭头函数的 this 指向,一样的语句语义完整差别。

new 挪用

在 es6 之前,每个函数都能够看成是组织函数,经由过程 new 挪用来发生新的对象(函数内无特定返回值的状况下)。而 es6 转变了这类状况,虽然 class 定义的类用 typeof 运算符获得的依然是 "function",但它不能像一般函数一样直接挪用;同时,class 中定义的要领函数,也不能看成组织函数用 new 来挪用。

而在 es5 中,用 new 挪用一个组织函数,会建立一个新对象,而个中的 this 就指向这个新对象。这没有什么牵挂,因为 new 自身就是设想来建立新对象的。

var data = "Hi"; // 全局变量
function AClass(data) {
this.data = data;
}
var a = new AClass("Hello World");
console.log(a.data); // Hello World
console.log(data); // Hi
var b = new AClass("Hello World");
console.log(a === b); // false

箭头函数中的 this

先来看看 MDN 上对箭头函数的申明

An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target. Arrow functions are always anonymous. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

这里已清晰了说清晰明了,箭头函数没有自身的 this 绑定。箭头函数中运用的 this,实际上是直接包括它的谁人函数或函数表达式中的 this。比方

const obj = {
test() {
const arrow = () => {
// 这里的 this 是 test() 中的 this,
// 由 test() 的挪用体式格局决议
console.log(this === obj);
};
arrow();
},
getArrow() {
return () => {
// 这里的 this 是 getArrow() 中的 this,
// 由 getArrow() 的挪用体式格局决议
console.log(this === obj);
};
}
};
obj.test(); // true
const arrow = obj.getArrow();
arrow(); // true

示例中的两个 this 都是由箭头函数的直接外层函数(要领)决议的,而要领函数中的 this 是由其挪用体式格局决议的。上例的挪用体式格局都是要领挪用,所以 this 都指向要领挪用的对象,即 obj

箭头函数让人人在运用闭包的时刻不须要太纠结 this,不须要经由过程像 _this 如许的局部变量来暂时援用 this 给闭包函数运用。来看一段 Babel 对箭头函数的转译能够能加深明白:

// ES6
const obj = {
getArrow() {
return () => {
console.log(this === obj);
};
}
}

// ES5,由 Babel 转译
var obj = {
getArrow: function getArrow() {
var _this = this;
return function () {
console.log(_this === obj);
};
}
};

别的须要注重的是,箭头函数不能用 new 挪用,不能 bind() 到某个对象(虽然 bind() 要领挪用没题目,然则不会发生预期结果)。不论在什么状况下运用箭头函数,它自身是没有绑定 this 的,它用的是直接外层函数(即包括它的近来的一层函数或函数表达式)绑定的 this

订正

  • this.onButtonClick 用于 jQuery 事宜的时刻,this 已被 jQuery 改成指向触发事宜的元素,谢谢 @玉轮哥哥 和 @QoVoQ 指出。此毛病已在文中修改了。

推荐阅读
  • MVVM架构~mvc,mvp,mvvm大话开篇
    返回目录百度百科的定义:MVP是从经典的模式MVC演变而来,它们的基本思想有相通的地方:ControllerPresenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模 ... [详细]
  • Spring框架入门指南:专为新手打造的详细学习笔记
    Spring框架是Java Web开发中广泛应用的轻量级应用框架,以其卓越的功能和出色的性能赢得了广大开发者的青睐。本文为初学者提供了详尽的学习指南,涵盖基础概念、核心组件及实际应用案例,帮助新手快速掌握Spring框架的核心技术与实践技巧。 ... [详细]
  • C#编程指南:实现列表与WPF数据网格的高效绑定方法 ... [详细]
  • 在Android平台上利用FFmpeg的Swscale组件实现YUV与RGB格式互转
    本文探讨了在Android平台上利用FFmpeg的Swscale组件实现YUV与RGB格式互转的技术细节。通过详细分析Swscale的工作原理和实际应用,展示了如何在Android环境中高效地进行图像格式转换。此外,还介绍了FFmpeg的全平台编译过程,包括x264和fdk-aac的集成,并在Ubuntu系统中配置Nginx和Nginx-RTMP-Module以支持直播推流服务。这些技术的结合为音视频处理提供了强大的支持。 ... [详细]
  • Django框架进阶教程:掌握Ajax请求的基础知识与应用技巧
    本教程深入探讨了Django框架中Ajax请求的核心概念与实用技巧,帮助开发者掌握异步数据交互的方法,提升Web应用的响应速度和用户体验。通过实例解析,详细介绍了如何在Django项目中高效实现Ajax请求,涵盖从基础配置到复杂场景的应用。 ... [详细]
  • 在MFC开发过程中,利用Windows内置的文件对话框可以显著提高文件操作的效率。本文总结了使用文件对话框进行文件选择和处理的经验,详细介绍了相关API的调用方法和参数设置,如`CFileDialog`类的使用、结构体`OPENFILENAME`的配置以及如何获取选中的文件路径。通过这些技巧,开发者可以快速实现文件的打开、保存等功能,提升应用程序的用户体验。 ... [详细]
  • 本文深入探讨了 JavaScript 中 `let` 关键词的特性和应用场景。与 `var` 不同,`let` 的作用域限制在代码块内,而 `var` 的作用域则限定在函数内部。此外,`let` 声明不会发生变量提升,并且在同一作用域内不允许重复声明同一变量。通过具体的示例和分析,本文详细解释了这些特性如何影响代码的可读性和维护性。 ... [详细]
  • Typora快捷键使用指南:提升写作效率的必备技巧 ... [详细]
  • $apply() 方法允许从 AngularJS 框架外部触发表达式的执行,确保其在 AngularJS 的上下文中运行。例如,当你使用 `setTimeout()` 或者集成第三方库时,可以通过调用 `$apply()` 来确保事件更新能够被 AngularJS 检测到并触发脏检查机制,从而实现数据的双向绑定。这一过程不仅保证了数据的一致性,还提升了应用的响应速度和用户体验。 ... [详细]
  • 手机上编写和运行PHP代码的最佳软件推荐 ... [详细]
  • 在Java应用中实现只读模式的切换方法与技巧 ... [详细]
  • 天猫导航案例分析:提升用户体验的设计策略
    通过对天猫双11狂欢节期间多个会场的导航设计进行深入分析,本文探讨了如何通过优化导航结构、增强用户界面的交互性和提高信息架构的清晰度,来显著提升用户的购物体验。具体案例包括服装、数码家电、家具建材、母婴童装、手机和美妆等会场,展示了多种有效设计策略的应用与效果。 ... [详细]
  • 火狐浏览器中使用JavaScript为audio标签的src属性赋值时遇到的问题及解决方案
    在火狐浏览器中,使用JavaScript为``标签的`src`属性赋值时可能会遇到兼容性问题。本文详细探讨了这一问题的成因,并提供了一种有效的解决方案,确保音频文件能够在火狐浏览器中正常播放。通过调整JavaScript代码,可以避免常见的加载失败或播放中断现象,提升用户体验。 ... [详细]
  • 使用PyQt5与OpenCV实现电脑摄像头的图像捕捉功能
    本文介绍了如何使用Python中的PyQt5和OpenCV库来实现电脑摄像头的图像捕捉功能。通过结合这两个强大的工具,用户可以轻松地打开摄像头并进行实时图像采集和处理。代码示例展示了如何初始化摄像头、捕获图像并将其显示在PyQt5的图形界面中。此外,还提供了详细的步骤说明和代码注释,帮助开发者快速上手并实现相关功能。 ... [详细]
  • 精通jQuery:深入解析事件处理机制与应用技巧
    本文详细探讨了jQuery的事件处理机制及其应用技巧,通过具体的代码示例,逐一解析了每个jQuery代码片段与其对应的HTML结构。文章以标记为基准,CSS作为通用样式,确保每段代码都能独立运行。HTML和CSS代码统一放置在文章末尾,方便读者参考和实践。 ... [详细]
author-avatar
盼抽淡了烟的悲伤
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有