JavaScript函数的this指向
前言
在常见的编程语言中,几乎都有this这个关键字(Objective-C中使用的是self),但是JavaScript中的this和常见面向对象语言中的this不太一样
- 常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。
- 也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象。
- 但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义
我们来看一下编写一个obj对象,有
this和没有this的区别
//不使用this
var obj = {
name:'aky',
running:function(){
console.log(obj.name + "running")
},
studying:function(){
console.log(obj.name + "studying")
}
}
//使用this
var obj = {
name:'aky',
running:function(){
console.log(this.name + "running")
},
studying:function(){
console.log(this.name + "studying")
}
}
this的指向什么呢?
-
我们先说一个最简单的,
this在全局作用域下指向什么?- 这个问题非常容易回答,在浏览器中测试就是指向
window
- 这个问题非常容易回答,在浏览器中测试就是指向
-
但是,在开发中很少直接在全局作用域下使用
this,通常都是在函数中使用 * 所有的函数在被调用时,都会创建一个执行上下文 * 这个而上下文记录这函数的调用栈、AO对象、作用域链等 * this也是其中的一条 -
我们先看一个令人困惑的问题:
- 定义一个函数,我们采用三种不同的方式进行调用,他产生了三种不同的结果
//定义一个函数 function foo(){ console.log(this) } //1、调用方式一:直接调用 foo() // window // 2、调用方式二:将foo放到一个对象obj中,在调用 var obj = { name:'aky', foo:foo } obj.foo() // obj对象 // 3、 调用方式三:通过call/apply来调用 foo.call("abc") // String abc 对象
- 定义一个函数,我们采用三种不同的方式进行调用,他产生了三种不同的结果
-
从上面案例中我们可以看出
- 在函数调用的时候,JavaScript会默认给
this绑定一个值 this的绑定和定义的位置没有关系this的绑定和调用方式以及调用的位置有关系this在运行的时候被绑定的 下面我们就来看看this的绑定规则
- 在函数调用的时候,JavaScript会默认给
绑定规则一:默认绑定
独立函数调用会默认绑定this
注:独立的函数调用我们可以理解函数没有绑定到某个对象上进行调用
//案例一:
function foo(){
console.log(this)
}
foo() //widow
//案例二:
function foo1(){
console.log(this)
}
function foo2() {
console.log(this)
foo1()
}
function foo3() {
console.log(this)
foo2()
}
foo3() // window window window
//案例三:
var obj = {
name: "why",
foo: function() {
console.log(this)
}
}
var bar = obj.foo
bar() // window
绑定规则二:隐式绑定
隐式绑定:也是就它的调用位置中,是通过某个对象发起的函数调用
//案例一
var obj = {
name: "why",
foo: foo
}
obj.foo() // obj对象
//案例二
var obj1 = {
name: "obj1",
foo: function() {
console.log(this)
}
}
var obj2 = {
name: "obj2",
obj1 :obj1
}
obj2.obj1.foo() //obj1对象
隐式绑定有一个前提:
- 必须在调用的对象内部有一个对函数的引用(比如一个属性)
- 如果没有这样的引用,在进行调用时,会报找不到该函数的错误
- 正式通过这个引用,间接的将
this绑定到了这个对象上
绑定规则三:显示绑定
我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用,怎么做呢?
call、apply、bind
通过call或者apply绑定this对象 显示绑定后,this就会明确的指向绑定的对象
function foo() {
console.log("函数被调用了", this)
}
var obj = {
name: "obj"
}
foo.call(obj) //obj对象
foo.apply(obj) // obj对象
foo.apply("aaaa") //String对象,存放aaaa
var newFoo = foo.bind(obj)
newFoo() // obj
绑定规则四:new绑定
JavaScript中的函数可以当作一个类的构造函数来使用,也就是说使用new关键字。 使用new关键字来调用函数时,会执行如下操作:
- 创建一个空对象
- 这个空对象的[[proto]]与构造函数的
protptype链接 - 这个新对象会绑定到函数调用的this上(this的绑定在这步骤完成)
- 如果函数没有返回值,返回新的对象
function Person(name){
this.name =name
}
Person.prototype.foo = function(){
console.log(this)
}
var p = new Person('aky')
p.foo() // p对象
规则绑定优先级
- 默认规则的优先级最低
- 显示绑定优先级高于隐式绑定
- new绑定的优先级高于隐式绑定
- new绑定优先级高于bind
- new绑定和call、apply是不允许同时使用,所以不存在优先级
- new绑定可以和bind一起使用,new优先级更高
this规则之外-ES6箭头函数
ES6中新增一个非常好用的函数类型:箭头函数,箭头函数不是同this的四种绑定规则(也是就不绑定this),而是根据外层作用域来决定this