♣ 为什么需要this?
在常见的编程语言中,几乎都有this这个关键字(Objective-C中使用的是self),但是JavaScript中的this和常见的面向对象语言中的this不太一样:常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中。也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象。但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义。
我们来看一下编写一个obj的对象,有this和没有this的区别
点击查看代码
// 从某些角度来说, 开发中如果没有this, 很多的问题我们也是有解决方案
// 但是没有this, 会让我们编写代码变得非常的不方便
var obj100 = {
name: "why",
eating: function() {
console.log(this.name + "在吃东西")
},
running: function() {
console.log(this.name + "在跑步")
},
studying: function() {
console.log(this.name + "在学习")
}
}
var info = {
name: "why",
eating: function() {
console.log(this.name + "在吃东西")
},
running: function() {
console.log(this.name + "在跑步")
},
studying: function() {
console.log(this.name + "在学习")
}
}
var person = {
name: "kobe",
eating: function() {
console.log(this.name + "在吃东西")
},
running: function() {
console.log(this.name + "在跑步")
},
studying: function() {
console.log(this.name + "在学习")
}
}
obj100.eating()
obj100.running()
obj100.studying()
♣ this指向什么呢?
我们先说一个最简单的,this在全局作用于下指向什么?
这个问题非常容易回答,在浏览器中测试就是指向window但是,开发中很少直接在全局作用于下去使用this,通常都是在函数中使用。所有的函数在被调用时,都会创建一个执行上下文:这个上下文中记录着函数的调用栈、AO对象等;this也是其中的一条记录;
点击查看代码
// 在大多数情况下, this都是出现在函数中
// 在全局作用域下
// 浏览器: window(globalObject)
// Node环境: {}
console.log(this)
// console.log(window)
node是空对象
- 1.函数在调用时,JavaScript会默认给this绑定一个值;
- 2.this的绑定和定义的位置(编写的位置)没有关系;
- 3.this的绑定和调用方式以及调用的位置有关系;
- 4.this是在运行时被绑定的;
那么this到底是怎么样的绑定规则呢?一起来学习一下吧
- 绑定一:默认绑定;
- 绑定二:隐式绑定;
- 绑定三:显示绑定;
- 绑定四:new绑定;
默认绑定
什么情况下使用默认绑定呢?独立函数调用。独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;
点击查看代码
// 默认绑定: 独立函数调用
// 1.案例一:
// function foo() {
// console.log(this)
// }
// foo()
// 2.案例二:
// function foo1() {
// console.log(this)
// }
// function foo2() {
// console.log(this)
// foo1()
// }
// function foo3() {
// console.log(this)
// foo2()
// }
// foo3()
// 3.案例三:
// var obj = {
// name: "why",
// foo: function() {
// console.log(this)
// }
// }
// var bar = obj.foo
// bar() // window
// 4.案例四:
// function foo() {
// console.log(this)
// }
// var obj = {
// name: "why",
// foo: foo
// }
// var bar = obj.foo
// bar() // window
// 5.案例五:
function foo() {
function bar() {
console.log(this)
}
return bar
}
var fn = foo()
fn() // window
var obj = {
name: "why",
eating: fn
}
obj.eating() // 隐式绑定
隐式绑定
另外一种比较常见的调用方式是通过某个对象进行调用的:也就是它的调用位置中,是通过某个对象发起的函数调用。
点击查看代码
// 隐式绑定: object.fn()
// object对象会被js引擎绑定到fn函数的中this里面
function foo() {
console.log(this)
}
// 独立函数调用
// foo()
// 1.案例一:
// var obj = {
// name: "why",
// foo: foo
// }
// obj.foo() // obj对象
// 2.案例二:
// var obj = {
// name: "why",
// eating: function() {
// console.log(this.name + "在吃东西")
// },
// running: function() {
// console.log(obj.name + "在跑步")
// }
// }
// // obj.eating()
// // obj.running()
// var fn = obj.eating
// fn()
// 3.案例三:
var obj1 = {
name: "obj1",
foo: function() {
console.log(this)
}
}
var obj2 = {
name: "obj2",
bar: obj1.foo
}
obj2.bar()
显示绑定
隐式绑定有一个前提条件:
- 必须在调用的对象内部有一个对函数的引用(比如一个属性);
- 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
- 正是通过这个引用,间接的将this绑定到了这个对象上;
如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?
JavaScript所有的函数都可以使用call和apply方法(这个和Prototype有关)。
其实非常简单,第一个参数是相同的,后面的参数,apply为数组,call为参数列表;这两个函数的第一个参数都要求是一个对象,这个对象的作用是什么呢?就是给this准备的。在调用这个函数时,会将this绑定到这个传入的对象上。因为上面的过程,我们明确的绑定了this指向的对象,所以称之为 显示绑定。
点击查看代码
// function foo() {
// console.log("函数被调用了", this)
// }
// 1.foo直接调用和call/apply调用的不同在于this绑定的不同
// foo直接调用指向的是全局对象(window)
// foo()
// var obj = {
// name: "obj"
// }
// call/apply是可以指定this的绑定对象
// foo.call(obj)
// foo.apply(obj)
// foo.apply("aaaa")
// 2.call和apply有什么区别?
function sum(num1, num2, num3) {
console.log(num1 + num2 + num3, this)
}
sum.call("call", 20, 30, 40)
sum.apply("apply", [20, 30, 40])
// 3.call和apply在执行函数时,是可以明确的绑定this, 这个绑定规则称之为显示绑定
如果我们希望一个函数总是显示的绑定到一个对象上,可以怎么做呢?
点击查看代码
function foo() {
console.log(this)
}
// foo.call("aaa")
// foo.call("aaa")
// foo.call("aaa")
// foo.call("aaa")
// 默认绑定和显示绑定bind冲突: 优先级(显示绑定)
var newFoo = foo.bind("aaa")
newFoo()
newFoo()
newFoo()
newFoo()
newFoo()
newFoo()
var bar = foo
console.log(bar === foo)
console.log(newFoo === foo)
♣ new绑定
JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。使用new关键字来调用函数是,会执行如下的操作:
- 1.创建一个全新的对象;
- 2.这个新对象会被执行prototype连接;
- 3.这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
- 4.如果函数没有返回其他对象,表达式会返回这个新对象;
点击查看代码
// 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
// this = 创建出来的对象
// 这个绑定过程就是new 绑定
function Person(name, age) {
this.name = name
this.age = age
}
var p1 = new Person("why", 18)
console.log(p1.name, p1.age)
var p2 = new Person("kobe", 30)
console.log(p2.name, p2.age)
var obj = {
foo: function() {
console.log(this)
}
}