前言
简单阐述
-
前端的初学者在学习Javascript中this指向的时候经常都会一头雾水,尤其是在ES6箭头函数出现之前。
-
this指向之所以容易让人头疼,原因在于 this 是在代码执行时根据环境和情况不同才决定绑定为什么值。
-
所以本篇文章主要是介绍和总结了各种情况下的this指向,如果文中有写的不对的地方或者明显错误,还请不吝指出互相交流。如果对你有帮助,也请不要吝啬手中的点赞~谢谢
在开始之前,还是需要介绍this的一些基础知识,具体如下:
- this 是javascript中的一个关键字,并非是一个变量。
- this 关键字指向的是一个对象,而指向哪个对象,或者是这个对象的值是什么取决于使用(调用)的方式和环境。
- this 指向的值是可以通过手动方式去改变的,比如call、bind、apply方法。
- this 在严格模式和非严格模式下也会有差别。
this的作用
- 从某些角度来说,开发中我们没有this,也是有解决方案的,但是如果真的没有this,会让我们编写代码十分不方便。
- 例如:
// 如果没有this,我们更改了obj的命名 // 那么下面的obj.name 都需要进行修改var obj = {var name = "malong"eating: function() {console.log(obj.name + "吃东西")}studying: function() {console.log(obj.name + "学习")}}
this的指向
- 在多数情况下,this出现在函数中,正常开发,通常都是在函数中使用this。
- 所有的函数在被调用时,都会创建一个执行上下文FEC
- 这个上下文中记录着函数的调用栈、AO对象等;
- this也是其中的一条记录;
- 函数在调用时,JavaScript会默认给this绑定一个值
- this的绑定和定义的位置(编写的位置)没有关系
- this的绑定和调用方式以及调用的位置有关系
this绑定方式1:默认绑定
独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用,这样this就是默认绑定
this是在运行时被绑定的
// 案例1 默认绑定
function foo() {console.log(this)
}// 案例2 默认绑定
function test1() {console.log(this)test2()
}function test2() {console.log(this)test3()
}function test3() {console.log(this)
}
test1()// 案例3 默认绑定
function bar() {func()
}var obj = {name: "malong",foo: function() {console.log(this)}
}bar(obj.foo)
this绑定方式2:隐式绑定
- 必须在调用的对象内部有一个对函数的引用(比如一个属性)
- 如果没有这样的引用,在进行调用时,会报找不到该函数的错误
- 正是通过这个引用,间接的将this绑定到了这个对象上
- 调用方式是通过某个对象进行调用的,隐式绑定
// 案例1 隐式绑定
function foo() {console.log(this)
}var obj = {name: "malong",foo: foo
}obj.foo()// 案例2 隐式绑定
function bar() {console.log(this)
}var obj1 = {name: "malong1",bar: bar
}var obj2 = {name: "malong2",obj1: obj1
}obj2.obj1.bar()// 案例3 隐式绑定
function baz() {console.log(this)
}var obj3 = {name: "malong",foo: baz
}var fn = obj3.foo;
fn()
this绑定方式3:显示绑定
- bind
- 形参第一个参数是对象,第二个参数是列表,返回值是一个函数。
- call
- 形参第一个参数是对象,第二个参数是列表
- apply
- 形参第一个参数是对象,第二个参数是数组 通过call绑定this
function foo() {console.log(this)}foo.call(window)foo.call({name: "malong"})foo.call(123)
如果我们希望一个函数总是显示绑定到一个对象上,而不用向上面那样写多次:
function foo() {console.log(this)}var obj = {name: "123"}var baz = foo.bind(obj);baz()
this绑定方式4:new绑定
- JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。
function Person(name) {console.log(this)this.name = name }var person = new Person("malong")
this的绑定过程:
- 创建一个新的对象
- 将构造函数的作用域赋值给新对象(构造函数.prototype === 对象.__ proto__)
- 执行构造函数中的代码(this绑定在这个步骤完成)
- 返回这个对象(内部有一个隐式的 return this)
this绑定优先级
new > 显示绑定(apply,call,bind) > 隐式绑定 > 默认
扩展【一】:this规则之外
无视显示绑定规则:
function foo() {console.log(this)
}var obj = {name: "malong"
}foo.call(obj)
foo.call(null)
foo.call(undefined)var baz = foo.bind(null)
baz()
间接函数引用:
function foo() {console.log(this)
}var obj1 = {name: "obj1",foo: foo
};var obj2 = {name: "obj2"
}obj1.foo();
(obj2.foo = obj.foo)();
箭头函数:
- 箭头函数没有this,它的this由运行时外层作用域的this决定
- 箭头函数没有arguments
- 箭头函数不能new
-
var name = "window";var obj = {name: "obj",getData: function() {// 会默认绑定,所以这里的this是 objsetTimeout(() => {console.log(this)}, 100)}}// 如果getData也是一个箭头函数,那么this会是windowobj.getData()
扩展【二】:this面试题
-
var name = "window"var person = {name: "person",sayName: function() {console.log(this.name);}};function sayName() {var sss = person.sayName;sss();person.sayName();(person.sayName)();(b = person.sayName)();}sayName()
var name = "window";var person1 = {name: "person1",foo1: function () {console.log(this.name)},foo2: () => console.log(this.name),foo3: function () {return function () {console.log(this.name)}},foo4: function () {return () => {console.log(this.name)}}}var person2 = { name: "person2" }person1.foo1();person1.foo1.call(person2);person1.foo2();person1.foo2.call(person2)person1.foo3()()person1.foo3.call(person2)()person1.foo3().call(person2)person1.foo4()()person1.foo4.call(person2)()person1.foo4().call(person2)
var name = 'window'function Person(name) {this.name = name;this.obj = {name: 'obj',foo1: function () {return function () {console.log(this.name)}},foo2: function () {return () => {console.log(this.name)}}}}var person1 = new Person('person1')var person2 = new Person('person2')person1.obj.foo1()()person1.obj.foo1.call(person2)()person1.obj.foo1().call(person2)person1.obj.foo2()()person1.obj.foo2.call(person2)()person1.obj.foo2().call(person2)