你应该要知道的JS中的this
前言
this 是 JavaScript 中不可不谈的一个知识点,它非常重要但又不容易理解。因为 JavaScript 中的 this 不同于其他语言。不同场景下的 this 指向不同(当函数被调用执行时会生成变量对象,确定 this 的指向,因此当前函数的 this 是在函数被调用执行的时候才确定的,所以导致 this 的指向灵活不确定),而且,在严格模式和非严格模式下,this 也会有不同的解读。
为什么要有 this
先想想如果 JavaScript 中没有 this 会怎么样?比如下面这段代码:
function identity(context) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = 'Hello, I am ' + identity(context)
console.log(greeting)
}
var you = {
name: 'Reader'
}
var me = {
name: 'Stone'
}
identity(you); // READER
speak(me); // Hello, I am Stone
我们给这 identity 和 speak 两个函数显示的传入了一个上下文对象,这似乎看不出什么,但是一旦你的应用变得越来越复杂,这种显示传递上下文就会让代码越来越混乱,代码结构越来越模糊。而使用 this 就可以避免这样,因为 this 提供了一种更优雅的方式来隐式传递对象引用,可以把 API 设计得更加简洁易用。
this 的绑定规则
全局对象中的 this
全局对象的变量对象是一个比较特殊的存在,在全局对象中,this 指向它本身,比如:
// this 绑定到全局对象
this.a1 = 10;
// 通过声明绑定到变量对象,全局环境中,变量对象就是它本身
var a2 = 20;
// 会隐式绑定到全局对象
a3 = 30;
console.log(a1); // 10
console.log(a2); // 20
console.log(a3); // 30
函数中的 this
在一个函数的执行上下文中,this 由该函数的调用者提供,由函数的调用方式来决定 this 的指向。下面这个例子:
function fn() {
console.log(this)
}
fn(); // Window {...}
默认全局对象就是调用者,等价于 window.fn()
(只讨论浏览器中全局对象)。但是在非严格模式中,this 是指向 undefined 的,比如:
'use strict';
function fn() {
console.log(this);
}
fn(); // undefined
window.fn(); // Window {...}
这就说明了,如果不指定函数调用者,在严格模式下回默认绑定到全局对象,在非严格模式下默认指向 undefined 。
函数是独立调用,还是被某个对象所调用,是很容易分辨的,比如:
'use strict';
var a = 20;
function fn() {
var a = 1;
var obj = {
a: 10,
c: this.a + 20
}
return obj.c
}
console.log(window.fn()); // 40
console.log(fn()); // TypeError
对象字面量的形式不会产生自己的作用域,所以 obj 中的 this.a
并不是指向 obj ,而是与函数内部的 this 一样。因此,当 window.fn()
调用时,fn 内部的 this 指向 window 对象,此时 this.a
访问全局对象中的 a ;由于是在严格模式中,在没有指明调用者的时候,fn 内部默认指向 undefined,所以在单独调用的时候会报错。
call/apply/bind 显示绑定 this
JavaScript 中提供了一个方式可以让我们手动指定函数内部的 this 指向,也就是前面提到的隐式传递对象,它们就是call/apply/bind。这三个方法是 Function 的原型方法,所有函数都可以调用这三个方法。看下面这个例子:
var a = 20;
var obj = {
a: 40
}
function fn() {
console.log(this.a);
}
fn(); // 20
fn.call(obj); // 40
fn.apply(obj); // 40
当函数调用 apply/bind 时,表示执行该函数,并且这个函数内部的 this 指向 apply/bind 的第一个参数。
二者的区别:
- call 的第一个参数是函数内部 this 的指向,后续的参数则是函数执行时所需要的参数,一个一个传递
- apply 的第一个参数与 call 相同,而执行函数的参数,则以数组的形式传入
bind 方法也能指定函数的 this ,但是它不同于call/apply。bind 方法会返回一个新函数,这个函数与原函数有相同的函数体,但是函数内部的 this 被绑定成 bind 方法的第一个参数,后续参数也是一个一个传入,并且不会自动执行新函数。
构造函数中的 this
可以把构造函数看成是普通函数,其中的 this 指向是创建的对象实例,之所以称之为构造函数,是因为我们会借助 new 操作符来调用函数。在用 new 创建对象时,this 指向发生改变是在第二步:
- 创建一个对象实例
- 将构造函数中的 this 指向这个对象
- 执行构造函数中的代码
- 返回这个新创建的对象
function Foo() {
this.a = 20
}
var foo = new Foo()
console.log(foo.a) // 20
箭头函数中的 this
箭头函数内部是不会绑定 this 的,它会捕获外层作用域中的 this,作为自己的 this 值。比如:
function Person() {
this.age = 20;
(() => {
this.age++
})()
}
var p = new Person()
console.log(p.age)
DOM事件中的 this
当函数被当做监听事件处理函数时, 其 this 指向触发该事件的元素 (针对于addEventListener事件)。比如:
<div class="box">
click
</div>
document.querySelector('.box').addEventListener('click', function(e) {
console.log(this) // 这个this指向div.box元素
}, false)
总结
this 的使用场景丰富多样,可以用来实现继承,实现函数柯里化等,作为开发者应该清楚各种使用方式以及其内部原理。
参考
来源:https://segmentfault.com/a/1190000017912451
你应该要知道的JS中的this的更多相关文章
- 很少有人知道的c++中的try块函数
c++有一些在现实世界中很少看到的结构.这些结构有着自己的用法,但是要特别小心保守的予以运用.就像是网站 The Old New Thing首页标题上面的说的那样: “代码通常被读的次数原因超过了被写 ...
- vue—你必须知道的 js数据类型 前端学习 CSS 居中 事件委托和this 让js调试更简单—console AMD && CMD 模式识别课程笔记(一) web攻击 web安全之XSS JSONP && CORS css 定位 react小结
vue—你必须知道的 目录 更多总结 猛戳这里 属性与方法 语法 计算属性 特殊属性 vue 样式绑定 vue事件处理器 表单控件绑定 父子组件通信 过渡效果 vue经验总结 javascript ...
- 5种你未必知道的JS和CSS交互的方法
随着浏览器不断的升级改进,CSS和JavaScript之间的界限越来越模糊.本来它们是负责着完全不同的功能,但最终,它们都属于网页前端技术,它们需要相互密切的合作.我们的网页中都有.js文件和.css ...
- 你应该知道的JavaScript中NaN的秘密
NaN,不是一个数字,是一种特殊的值来代表不可表示的值,使用typeof或其他任何与之比较的处理方式,‘NaN’则会引起一些混乱, 一些操作会导致NaN值的产生.这里有些例子: Math.sqrt(- ...
- 迟早要知道的JS系列之常用数组方法
常用数组方法 一.不会改变原始数组的方法: 即访问方法,下面的这些方法绝对不会改变调用它们的对象的值,只会返回一个新的数组或者返回一个其它的期望值. 1. concat() ** 语法:** Java ...
- JS中的继承(上)
JS中的继承(上) 学过java或者c#之类语言的同学,应该会对js的继承感到很困惑--不要问我怎么知道的,js的继承主要是基于原型(prototype)的,对js的原型感兴趣的同学,可以了解一下我之 ...
- JS中数值类型的本质
一.JS中的数值类型 众所JS爱好友周知,JS中只有一个总的数值类型--number,它包含了整型.浮点型等数值类型.其中,浮点数的实现思想有点复杂,它把一个数拆成两部分来存储.第一部分是有效位数,也 ...
- react.js 你应知道的9件事
React.js 初学者应该知道的 9 件事 本文假定你已经有了一下基本的概念.如果你不熟悉 component.props 或者 state 这些名词,你最好先去阅读下官方起步和手册.下面的代码 ...
- 2018-11-20-UWP-开发中,需要知道的1000个问题
title author date CreateTime categories UWP 开发中,需要知道的1000个问题 lindexi 2018-11-20 09:28:53 +0800 2018- ...
随机推荐
- String变量的两种创建方式
在java中,有两种创建String类型变量的方式: String str01="abc";//第一种方式 String str02=new String("abc&qu ...
- vue+axios+easy-mock+element-ui实现表格分页功能
废话不多,效果如图: LineSource.vue文件内代码如下: <template> <div class="c-main auth userControl" ...
- OPENCV VS设置
OPENCV VS设置 第一步 工程->工具->选项->VC++目录 第二步 这两项放到系统path下 D:\OpenCV2.4.3\VS\bin\Debug;D:\OpenCV2. ...
- 01_Quartz基础结构
[各种任务调度的使用场景] 论坛每天凌晨统计论坛用户的积分排名. 论坛每半个小时生成精华文章. 每隔30分钟对锁定过期的用户解锁. 每月1号统计上个月各部门的业务数据. [Quartz 简介] Qua ...
- office转换为html在线预览
/// <summary> /// word 转换为html /// </summary> /// <param name="path">要转换 ...
- 重复启动某一款应用,并传递intent参数
Intent intent = getPackageManager().getLaunchIntentForPackage(packageName); intent.setFlags(Intent.F ...
- Hadoop ->> MapReduce编程模型
对于MapReduce模型的实现,有Java等一些语言实现了接口,或者用像Hive/Pig这样的平台来操作.MapReduce由Map函数.Reduce函数和Main函数实现.第一步,源数据文件按默认 ...
- Excel VBA 复制
将 Sheet1 复制到 Sheet3 后面时,实现方法如下: Worksheets("Sheet1").Copy After:=Worksheets("Sheet3&q ...
- Laravel 开源电商体验与部署
体验 开源项目已经部署了体验环境,开源通过扫描下方小程序码进行体验: 我们部署了 Laravel API demo 环境,访问地址:https://demo-open-admin.ibrand.cc/ ...
- NodeJS服务器端平台实践记录
[2015 node.js learning notes]by lijun 01-note Nodejs是服务器端的javascript,是一种单线程.异步I/O.事件驱动型的javascript:其 ...