一、this到底是什么
- 学习this的第一步是明白this既不指向函数自身也不指向函数的词法作用域,this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
二、this的四种绑定规则
-
默认绑定
默认绑定一般发生在回调函数,函数直接调用;
function fn() { console.log( this.a ) } let a = 2; fn(); //2this.a被解析成了全局变量a,函数调用时应用了this的默认绑定,因此this指向全局对象。在代码中,fn是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。
如果使用严格模式(use strict),则不能将全局对象用于默认绑定,因此this会绑定到undefined
-
隐式绑定
通俗来说谁最终调用函数,this指向谁
const obj = { name : 'lyq', getName () { console.log( this ); //obj console.log( this.name ); //lyq } }; obj.getName();链式调用只有最后一层在调用位置中起作用
var obj2 = { name:'lyq', foo() { console.log( this.name ) } } var obj1 = { name:'zs', obj2:obj2 } obj1.obj2.foo(); // lyq隐式丢失:
一个最常见的this绑定问题是被隐式绑定的函数会丢失绑定对象,也就是你说它会应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否是严格模式
var a = 100; var obj = { a : 2, foo() { console.log( this.a ) } } var bar = obj.foo; bar(); //100obj.foo 是引用属性,赋值给bar的实际上就是foo函数(即:bar指向foo本身,因此应用了默认绑定,指向全局属性a
-
显示绑定call,apply,bind
相对隐式绑定,this值在调用过程中会动态变化,可是我们就想绑定指定的对象,这时就用到了显示绑定。
显示绑定主要是通过改变对象的prototype关联对象。具体使用上,可以通过这两个方法call(…)或apply(…)来实现(大多数函数及自己创建的函数默认都提供这两个方法)。
function foo() { console.log( this.a ) } var obj = { a : 2 } foo.call( obj ) // 2call和bind是一样的,区别在于参数的设置上。
硬绑定
function foo() { console.log( this.a ); } var a = 2; var obj1 = { a: 3, }; var obj2 = { a: 4, }; var bar = function() { foo.call( obj1 ); } setTimeout( bar, 100 ); // 3 bar.call( obj2 ); //3这里需要注意下,虽然bar被显示绑定到obj2上,对于函数bar 中的this确实被绑定到了obj2,而foo因为通过foo.call( obj1 )已经显示绑定了obj1,所以在foo函数内,this指向的是obj1,不会因为bar函数内指向obj2而改变自身。所以打印的是obj1.a(即3)。
-
new绑定
function foo(a) { this.a = a; } var a = 2; var bar1 = new foo(3); console.log(bar1.a); // 3使用new来调用foo()时,会创造一个新对象并把它绑定到foo()调用中的this上。
三、箭头函数this指向
首先需要明确,箭头函数不适用以上this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this
箭头函数this的定义:箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定 。
var name = 'lyq'; var obj = { name : 'zs', getName:function() { console.log( this.name ) } } obj.getName(); //zsvar name = 'lyq'; var obj = { name : 'zs', getName:()=>{ console.log( this.name ) } } obj.getName(); //lyq所谓的定义时候绑定,就是this是继承自父执行上下文中的this,比如这里的箭头函数中的this.name,箭头函数本身所在的对象为obj,而obj的父执行上下文就是window,因此这里的this.name 实际上表示的是window.name,因此输出的是lyq。(this只有在函数被调用,或者通过构造函数new Object()的形式才会有this)
注意简单对象(非函数)是没有执行上下文的!
在setInterval和setTimeout中传入函数时,函数中的this会指向window对象。箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。
export default { data () { return { name: zs, } }, methods: { setName () { setTimeout(() => { this.name = 0; }, 500); } } }此时函数的this指向的是定义它的时候的对象,也就是this指向了data内中对应的变量。如果使用function,this则会指向window,无法获得当前对象。