1.普通函数与箭头函数中的this
<1>普通函数
- 通常情况下(非严格模式,若没使用 'use strict'),没找到直接调用者,则this指的是 window ;
- 严格模式,没有直接调用者的函数中的this是 undefined;
- this代表它的直接调用者(js中的this是执行上下文), 例如 obj.fun ,fun中的this就是obj;
- 使用call,apply,bind(ES5新增)绑定的,this指的是绑定的对象;
<2>箭头函数
- 箭头函数无自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),此处指父级作用域,而不是执行时的对象, 定义它的时候,可能环境是window; 箭头函数可以让我们在 setTimeout ,setInterval中方便的使用this;
- 在箭头函数中this指向的固定化,并非箭头函数内部有绑定this的机制,而是箭头函数无自己的this,导致内部的this就是外层代码块的this。
2.this指向
每个函数都会有自身的this,但是this并不是在函数声明完就绑定到某个对象上的,只有在函数调用时,this才会被绑定;因此,this的绑定和函数定义的位置没有关系,和函数的调用方式有关系。
常见的this指向:
- 全局作用域中或者普通函数中this指向全局对象window;
- 立即执行函数this必定指向window;
- 定时器this指向window;
- 事件中this指向事件源对象;
- 构造函数中this指向对象实例;
- 方法中谁调用就指向谁;
3.this绑定规则
(1)默认绑定规则
<1>默认绑定全局对象window
console.log(this === window); //true
<2>全局作用域下独立调用函数,this指向window
function exam() {
console.log(this===window); //true
}
exam();
(2)隐式绑定规则
通俗的讲,可以理解为谁调用就指向谁;
<1>对象内调用方法
let obj = {
name: 'chuichui',
foo: function () {
console.log(this); //this指向obj
}
}
obj.foo()
<2>修改
let obj = {
name: 'chuichui',
foo: function () {
console.log(this); //chuichui
function exam() {
console.log(this); //window 为什么? 因为exam独立调用
}
exam()
}
}
obj.foo()
<3>隐式丢失
let obj = {
name: 'chuichui',
foo: function () {
console.log(this); //window 为什么不是obj? bar拿到obj.foo的引用,然后在全局下独立调用
}
}
let bar = obj.foo
bar()
<4>函数作为参数
function foo() {
console.log(this); //window obj.foo在bar函数内独立调用
}
function bar(fn) {
fn()
}
let obj = {
name: 'chuichui',
foo: foo
}
bar(obj.foo)
补充:
函数作为参数时,参数函数叫做子函数,外面的函数叫父函数,子函数也叫回调函数,比如forEach 、setimeout,这些函数里的参数函数也叫内置参数;
注:父函数有能力决定子函数this的指向,例如forEach里第一个参数是一个函数,第二个参数就是this绑定的对象,不写默认绑定window;
(3)显式绑定
call、apply、bind返回一个新函数,新函数指向绑定的对象,旧函数不会;
call、apply、bind:
1>相同点:均可以改变函数内部的this指向;
2>不同点:
- call和apply会调用函数,并且改变函数内部this的指向;
- call和apply传递的参数不一样,call传递参数aru1,aru2…形式 , apply必须数组形式;
- bind 不会调用函数,可以改变函数内部this指向;
3>主要应用场景:
- call常做继承;
- apply常跟数组有关系,如借助于数学对象实现数组最大值最小值;
- bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向;
(4)new绑定
this指向函数实例化之后的对象;
(5)绑定规则优先级
优先级顺序由高到底依次为:new绑定>显式绑定>隐式绑定>默认绑定