有关this
this
是Javascript
函数内部的一个特殊对象,引用的是函数运行时的环境对象,也就是说,this
是动态的(箭头函数除外),是在运行时进行绑定的,并不是在编写时绑定(箭头函数是编写时绑定)。 this
的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
绑定规则
this
绑定根据函数的调用方式基本上有四种规则:
全局性调用
函数的最通常用法,this
代表全局对象:
function sayColor() {
console.log(this.color)
}
var color = 'red'
sayColor() // red
作为对象的方法调用
函数作为某个对象的方法调用时,this
指向这个上级对象:
let car = {
color: 'black',
sayColor
}
car.sayColor() // black
当存在多重调用时,最后一层会决定调用位置:
let house = {
color: 'white',
car
}
house.car.sayColor() // black
使用apply、call、bind方法绑定调用对象
函数可以使用apply、call、bind
方法来改变调用对象。这些方法的第一个参数是一个对象,它们会把这个对象绑定到this
:
let bindObj = {
color: 'green'
}
sayColor.apply(bindObj) // green
sayColor.call(bindObj) // green
sayColor.bind(bindObj)() // green
第一个参数为空时,默认绑定全局对象:
sayColor.apply() // red
构造函数调用
当函数作为构造函数被调用时,this
指向创建的新对象:
function Person(color) {
this.color = color
}
let p = new Person('yellow')
p.color // yellow
使用new操作符调用构造函数时,首先会创建一个新对象,然后将构造函数的作用域赋给新对象,this
就指向了新对象,过程与下面代码类似:
let o = new Object()
Person.call(o, 'yellow')
o.color // yellow
当然,构造函数不通过new调用时,与普通函数无区别,可以理解为实际上并不存在所谓的构造函数,只有对于函数的构造调用:
Person('yellow')
// this指向了全局对象window
window.color // yellow
以上就是this
绑定的四种规则,函数调用时,先找到调用位置,然后判断需要应用四条规则中的哪一条。
应用
现在来看下实际应用的几种特殊情况。
回调函数
当做回调函数调用时,一般是全局调用:
setTimeout(sayColor) // red 指向window
let obj = {
color: 'green',
say () {
sayColor()
}
}
obj.say() // red 指向window
但在一些上下文中会进行隐式绑定,比如事件中的this
是指向于事件的目标元素的,还有一些数组的操作方法可以使用第二个参数来绑定this
:
[1, 2, 3].forEach(function () { console.log(this.color) }) // red red red 指向window
[1, 2, 3].forEach(function () { console.log(this.color) }, car) // black black black 指向car对象
绑定丢失
function sayColor() {
console.log(this.color)
}
let car = {
color: 'black',
sayColor
}
var color = 'red'
let alias = car.sayColor
alias() // red
上述代码中将对象的方法赋值给新变量alias
,alias
函数执行时,this
指向了全局对象。
函数的名字仅仅是一个包含指向函数对象的指针的变量,car对象的sayColor属性保存在一个属性描述符对象中:
Object.getOwnPropertyDescriptor(car, 'sayColor')
// {value: ƒ, writable: true, enumerable: true, configurable: true}
其中描述符对象的value属性保存了指向sayColor函数的指针,let alias = car.sayColor
语句将指向sayColor函数的指针赋值给变量alias
,执行alias
函数就是在全局对象中执行函数sayColor
。
当做回调函数时:
setTimeout(car.sayColor, 500) // red 指向window
因为Javascript中的函数参数都是按值传递的,上述代码将指向sayColor
函数的指针赋值给了setTimeout
函数的参数,也相当于在全局环境中执行sayColor
函数。
闭包
匿名函数的执行一般具有全局性,在闭包中由于编写方式可能不会那么明显:
let obj = {
color: 'green',
say () {
return function() {
console.log(this.color)
}
}
}
obj.say()() // red this指向全局window
内部匿名函数是有自己的this
变量的,所以无法访问到外部函数say
的this
变量,我们可以将外部this
变量保存于一个闭包能够访问的变量之中:
let obj = {
color: 'green',
say () {
let self = this
return function() {
console.log(self.color)
}
}
}
obj.say()() // green
当然,现在可以用箭头函数来绑定this:
let obj = {
color: 'green',
say () {
return () => {
console.log(this.color)
}
}
}
obj.say()() // green
优先级
全局性调用优先级是最低的。
使用apply
等函数绑定this
的优先级高于对象调用:
let car = {
color: 'black',
sayColor
}
let bindObj = {
color: 'green'
}
car.sayColor() // black
car.sayColor.apply(bindObj) // green
使用new
操作符绑定高于使用apply
等函数:
function Person(color) {
this.color = color
}
let obj = {}
let bindPerson = Person.bind(obj)
let p = new bindPerson('yellow')
p.color // yellow this指向了创建的对象p
obj.color // undefined 没有指向obj
箭头函数与this
ES6中引进了箭头函数,可以简化匿名函数的语法:
setTimeout(() => { console.log(this.color) }, 50)
箭头函数内部是没有this
、arguments
、super
、new.target
特殊变量的,访问它们时会指向最近的外层非箭头函数的相应变量:
function sayColor() {
return () => {
return () => {
console.log(this.color)
}
}
}
sayColor.call({ color: 'red' })()() // red 指向了外层sayColor函数的this对象
sayColor.call({ color: 'red' }).call({ color: 'green' })() // red 依然指向外层sayColor函数的this对象
sayColor.call({ color: 'red' }).call({ color: 'green' }).call({ color: 'yellow' }) // red箭头函数使用call是无法绑定this的
所以,箭头函数可以起到固定化this
指向的效果,一定程度上可以说this
是静态的,参考上面闭包的代码:
// ES6箭头函数
let obj = {
color: 'green',
sayColor () {
return () => {
console.log(this.color)
}
}
}
// ES5
let obj = {
color: 'green',
sayColor () {
let self = this
return function() {
console.log(self.color)
}
}
}
当然,静态并不意味着箭头函数的this
是永远不变的,而是随着外层函数的this
变化而变化:
let obj = {
color: 'green',
sayColor () {
return () => {
console.log(this.color)
}
}
}
obj.sayColor()() // green
obj.sayColor.call({ color: 'red' })() // red
不适用情况
在事件中想将this
指向目标元素时,箭头函数是不适用的:
btn.addEventListener('click', () => {
console.log(this)
})
上述代码中this
指向了全局对象,而不是事件的目标元素。
将函数当做对象的方法调用并且想将this
指向对象时,也是不适用的:
let obj = {
color: 'green',
sayColor: () => {
console.log(this.color)
}
}
上述代码中this
也指向了全局对象。
总之,需要this
动态时使用非箭头函数,需要this
静态时使用箭头函数:
function Person() {
this.color = 'yellow'
setTimeout(() => { console.log('person color is',this.color) }, 50)
setTimeout(function() { console.log('global color is',this.color) }, 50)
}
var color = 'red'
new Person()
// 输出
person color is yellow
global color is red
随机推荐
- windows下创建.gitignore
网上搜索 .gitignore 的创建,很多linux上的,而且还一样,... 尝试了几次,windows可以这样写 .svn/ .settings/ .buildpath .project
- shell 字符串比较 算数比较 文件条件测试
set-group-id即set-gid -->授予了程序其所在组的访问权限 set-user-id即set-uid -->授予了程序其拥有者的访问权限而不是其使用者的访问权限 set-g ...
- python pip出错问题解决记录
今天安装一下requests模块,遇到网络问题 pip install requests Retrying (Retry(total=4, connect=None, read=None, redir ...
- 【托业】【怪兽】TEST01
101. respectable 值得尊敬的(形容人或事物) respectful 态度恭敬的(形容人) respecting 关于…… respective 各自的 102. hardly 几乎没有 ...
- Java面试题和解答(三)
1.这段代码大多数情况下运行正常,但是某些情况下会出问题.什么时候会出现什么问题?如何修正? public class MyStack { private List<String> lis ...
- 2018-2019-2 《网络对抗技术》Kali安装 Week1 20165236
2018-2019-2 <网络对抗技术>Kali安装 Week1 20165236 一.kali 下载 安装 网络 共享 软件源 二.安装步骤 1.官网上下载kali Linux: 2.参 ...
- 2019春第七周作业Compile Summarize
这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 在这里 我在这个课程的目标是 能更加进一步的够熟练掌握指针的用法 这个作业在那个具体方面帮助我实现目标 指针对于基础题目的做法 参考文献与 ...
- 3.2.2 SpringMVC配置式开发
SpringMVC配置式开发 1. SpringMVC运行原理(执行过程) 2. 需求 用户提交一个请求, 服务端处理器接收到请求后, 给出一条信息,在相应页面中显示该条信息 3. 开发步骤 (1) ...
- Pubmed/PMC/Meline的异同点【转载】
转自:http://paper.dxy.cn/article/495034 一.PubMed.PMC 和 MEDLINE 到底有什么区别和联系? 可以看出,PubMed的收录范围是最广的:三个都是生物 ...
- 极客时间 深入拆解java虚拟机 一至三讲学习总结
为什么要学习java虚拟机 1.学习java虚拟机的本质,是了解java程序是如何被执行且优化的.这样一来,才可以从内部入手,达到高效编程的目的.与此同时,你也可以为学习更深层级.更为核心的java技 ...