一.定义
this
是javascript中的一个关键字this
会在执行上下文绑定一个对象this
在不同的执行条件下会绑定不同的对象this
永远指向最后调用它的那个对象this
函数执行过程中,this
一旦被确定了,就不可以再更改
二.绑定规则
- 默认绑定
- 隐式绑定
- call/apply/bind显示绑定
- new绑定
2.1.默认绑定
独立的函数调用,我们可以理解成函数没有被绑定到某个对象上进行调用。
scss
function foo() {
console.log(this); // window
}
foo()
- foo前面没有调用的对象就是全局对象
window
, 这就相当于window.a()
- 严格模式下,不能将全局对象用于默认绑定,
this
会绑定到undefined
2.2.1隐式绑定
函数还可以作为某个对象的方法调用,通俗点就是,对象拥有某个方法,通过这个对象访问方法直接调用,这时this
就指这个上级对象
javascript
const person = {
name: 'maomao',
foo: function() {
console.log(this.name); // 'maomao'
}
}
person.foo()
- fn被person发起调用,进行了隐式绑定,那么
this
会隐式的被绑定到obj对象上
2.2.2隐式丢失
案例一:
csharp
function foo() {
console.log(this.name); // 'undefined'
}
const person = {
name: 'maomao',
foo: foo
}
const fn = person.foo
fn()
- 这里并没有直接调用,而是通过
person
找到对应的fn
的内存地址,直接赋值给变量p
- 然后通过
p
直接调用,没有绑定任何的对象,相当于默认绑定 this
也就是全局对象window
,window
中取出name
属性,必定为undefined
案例二:
php
var name = 'haohao'
const person = {
name: 'maomao',
foo: function() {
console.log(this.name); // 'haohao'
}
}
function bar(fn) {
fn()
}
bar(person.foo)
- 首先
bar
中的fn
为一个回调函数 fn=person.foo
参数传递就是隐式赋值,他们都是指向fn=person.foo
引用,也就是他们的内存地址- 因为他们的
this
丢失,也就是函数独立调用,默认绑定规则,this
为全局的window
对象 - 但是有些场景,不想让隐式丢失,就出现显示绑定。
2.3显示绑定
在某些场景下,this
的改变都是意想不到。那么我们可以,javascript所有的函数都可以使用call、apply、bind方法
2.3.1 call/apply
call()
第一个参数为固定绑定的this对象,call()
第二个参数及第三个第四个等等参数,都作为参数传递给所调用的函数。call()
与apply()
第一个参数相同的,后面的参数,call
是参数列表,apply
是数组。- 在调用这个函数时,会将
this
绑定到这个传入的对象上。call()
javascript
var name = 'haohao'
const person = {
name: 'maomao',
foo: function(age, height) {
console.log(this.name, age, height); // 'maomao, 18, 1.88'
}
}
function bar(fn) {
fn.call(person, 18, 1.88)
}
bar(person.foo)
apply()
javascript
var name = 'haohao'
const person = {
name: 'maomao',
foo: function(age, height) {
console.log(this.name, age, height); // 'maomao, 18, 1.88'
}
}
function bar(fn) {
fn.apply(person, [18, 1.88])
}
bar(person.foo)
2.3.2 bind
bind
与call
,第一个参数和第二个及以后的参数相同,不同的是返回一个this
绑定后的函数,调用返回后端函数,就可以拿到期望的this
javascript
var name = 'haohao'
const person = {
name: 'maomao',
foo: function(age, height) {
console.log(this.name, age, height); // 'maomao, 18, 1.88'
}
}
function bar(fn) {
let f = fn.bind(person, 20, 1.88)
f()
}
bar(person.foo)
2.3.3 内置函数
- 我们会调用一些javascript的内置函数,或者一些第三方库中的内置函数。
- 这些内置函数会要求我们传入另外一个函数;
- 我们自己并不会显示的调用这些函数,而且javascript内部或者第三方库会帮助我们执行;
案例一:setTimeout
javascript
setTimeout(function() {
console.log(this)
}, 1000)
setTimeout
中会传入一个函数,这个函数中的this通常是window- 这个和
setTimeout
源码的内部调用有关。 setTimeout
内部是通过apply
进行绑定的this
对象,并且绑定的是全局对象;
案例二:数组的forEach
javascript
const names = ['aaa', 'bbb', 'ccc']
names.forEach(function(item) {
console.log(this); // window
})
forEach
中传入的函数打印的也是window
对象;- 默认情况下传入的函数是自动调用函数(默认绑定)
- 我们可以改变该函数的this指向, 如下;
javascript
const names = ['aaa', 'bbb', 'ccc']
const obj = {
name: 'maomao'
}
names.forEach(function(item) {
console.log(this); // window
}, obj)
案例三:div的点击
javascript
const box = document.querySelector('.box')
box.onclick = function() {
console.log(this); // box对象
}
- 点击的时候,执行传入的回调函数被调用时,会将box对象绑定到该函数中
2.4new绑定
javascript中的函数可以当做一个类型构造函数来使用,也就是使用new关键字。 使用new关键字来调用函数时,会执行如下的操作;
- 1.创建一个新对象;
- 2.这个对象的[[prototype]]指向构造函数的
prototype
属性; - 3.this指向了这个新对象;
- 4.如果函数没有返回其他对象,那么就返回这个新对象;
javascript
// 创建Person
function Person(name) {
console.log(this); // Person {}
this.name = name // Person {name: 'maomao'}
}
var p = new Person('maomao')
console.log(p);
三.规则优先级
3.1.默认规则的优先级最低
存在其他规则时,就会通过其他规则的方式来绑定this
3.2.显示绑定优先级>隐式绑定
javascript
function foo() {
console.log(this);
}
const obj1 = {
name: 'obj1',
foo: foo
}
const obj2 = {
name: 'obj2',
foo: foo
}
// 隐式绑定
obj1.foo() // obj1
obj2.foo() // obj2
//隐式绑定和显示绑定同时存在
obj1.foo.call(obj2) // obj2, 说明显示绑定优先级更高
3.4.new绑定优先级>bind
new
绑定和call
、apply
是不允许同时使用的
javascript
function foo() {
console.log(this);
}
const obj = {
name: "why",
foo: foo
}
const fn = foo.bind(obj)
const bar = new fn() //foo
3.5优先级
- new绑定 > 显示绑定(bind)> 隐式绑定 > 默认绑定
四.ES6箭头函数
- 箭头函数时ES6新增的语法
ini
const foo = () => {}
- 箭头函数不绑定this, 根据外层作用域来决定this.
案例一:
javascript
const obj = {
data: [],
getData: function() {
setTimeout(() => {
console.log(this) // obj
}, 1000)
}
}
obj.getData()
案例二:
javascript
const obj = {
data: [],
getData: () => {
setTimeout(() => {
console.log(this) // window
}, 1000)
}
}
obj.getData()
- 箭头函数不绑定this, 不断的从上层作用域找,那么找到了全局作用域;
- 在全局作用域内,this代表的就是window