概述
- this是一个JavaScript的一个很重要的关键字,他与var、let、function相比比较复杂,同时也比较灵活。
- this会在上下文中绑定一个对象,this绑定的对象有可能是window、Object对象、function方法等等。 根据某些规则进行绑定,那么这些规则又是什么呢?
理解this
1. 为什么使用this
- 常见的面向对象语言中,this通常只会出现在
类的方法中
。- 你需要有一个类,类中的方法,
this代表当前调用对象
。- JavaScript中的this
出现的位置不同、代表的含义
都会有不同的变化(灵活)。
2. this优化代码前后对比
var obj = {
name: '小韩',
running: function () {
console.log(obj.name + '跑起来');
}
}
obj.running() // 打印出:(obj.name = 小韩) 跑起来
不使用this的时候,通过obj的引用获取内容
弊端: 定义的obj中如果有很多次obj引用,那么当改变obj为info的时候就要全部更换,很麻烦。
var obj = {
name: '小韩',
running: function () {
console.log(this.name + '跑起来');
}
}
obj.running() // 打印出:(this.name = 小韩) 跑起来
此时这个时候,不管obj是否更换,obj对象的方法不用更换。 (!!!此时 this = obj)
3. this的使用可以让我们的更加便捷的引用对象,使代码可以更加简洁和易于复用;
4. this指向什么?
- 在浏览器中测试就是指向
window
;- 所以我们可以认为在
全局的情况下
, this = window。注意:开发中很少在全局环境下使用this,一般都是在方法中使用。
5. 所有的函数在被调用的时候,都会创建一个执行上下文:
- 这个上下文中记录着
函数的调用栈、函数的调用方式、传入的参数信息等;
- this也是其中的一个属性。
6. 案例1: 定义一个函数,三种方式调用,产生三种结果:
function han() {
console.log(this)
}
han() // 1.直接调用 : 输出 window
var obj = {
name: 'why',
han: han
}
// 2. 将han放到一个对象中,在调用
obj.han() // obj 对象 : 输出obj这个对象
han.call('abc') //3.通过call/apply调用 :输出 String{"abc"} 字符串对象
启示
- 函数在调用时候,JavaScript会默认给this绑定一个值;
- this的绑定和定义的位置没有关系;
- this的绑定和调用方式以及调用的位置有关系;
- this是在运行时被绑定的。
this的绑定规则
this就是一个在被调用时候被绑定的一个对象。
1. 默认绑定
- 情景: 独立函数调用 使用默认绑定;
- 定义: 函数没有被绑定到某个对象上,这叫做独立函数调用。
案例一:普通函数调用
- 直接被调用,没有进行对象关联
- 函数中的this指向window。
function foo() {
console.log(this);
}
foo(); // window
案例二: 函数调用链(一个函数调用另外一个函数)
- 所有函数都没有被绑定到某个对象上
function test2(){
console.log(this) // window
}
function test1() {
console.log(this) // window
test2()
}
test1()
案例三: 将函数作为参数传入到另外一个函数中
function test2(func){
func()
}
function test1() {
console.log(this) // window
}
test2(test1)
2. 隐式绑定
- 情景: 通过某个对象进行调用这个方法;
- 定义: 在它的调用位置中,是通过某个对象发起的函数调用;
- 前提条件: 必须在调用的
对象内部
有一个对函数的引用;- 没有这个引用会报错(调用时,找不到该函数) ,通过这个引用,间接的将this绑定到这个对象上。
案例一:通过对象调用函数
- han()的调用位置是obj.han()方式进行调用;
- 那么han()调用的时候this会隐式的绑定obj对象。
function han() {
console.log(this)
}
var obj = {
name: '小韩啊',
han: han
}
obj.han() // {name:'小韩啊',han: f} 输出 obj 对象
案例二:升级案例一
- 实际上还是obj1绑定了this。
function han() {
console.log(this)
}
var obj1 = {
name: '小韩啊',
han: han
}
var obj2 = {
name:'2个小韩啊',
han:obj1
}
obj2.han.han(); // 谁点函数 谁绑定 其他的都是引用
案例三:隐式丢失
- foo()最终是通过**bar()**进行的调用,bar()在进行调用的时候没有绑定任何的对象;
- 因此,bar并没有形成隐式绑定;
- 这就相当于是默认绑定。
function foo() {
console.log(this);
}
var obj1 = {
name: "obj1",
foo: foo
}
// 讲obj1的foo赋值给bar
var bar = obj1.foo;
obj1.foo(); // obj1对象
bar(); // window
3. 显示绑定
- 情景: 不希望在对象内部包含对这个函数的引用,希望这个对象强制调用;
- JavaScript所有的函数都可以使用call和apply方法
和Prototype有关
;- call 和 apply 两个函数 第一个参数相同,后面的参数:apply为数组,call为参数列表
- 这两个函数第一个参数都要求是一个对象, 这个对象的作用是给this准备的
- 在调用这个函数的时候,会将this绑定到这个传入的对象上。
- 定义: 在上面的过程中,明确的绑定了this指向的对象,称为显示绑定。
1. 通过call或者apply绑定this对象
function han(){
console.log(this)
}
han.call(window) // window
han.call({name : '韩'}) // {name:'韩'}
han.call(123) // Number对象 存放123
2. bind函数
情景:希望一个函数总是显示的绑定到一个对象上面,如何做?
- 方案1: 手写bind辅助函数
这个bind辅助函数的目的是在执行han函数的时候,总是让它的this绑定到obj对象上。
function han() {
console.log(this)
}
var obj = {
name: '韩'
}
function bind(func, obj) {
return function () {
return func.apply(obj, arguments)
}
}
var bar = bind(han,obj);
bar(); // {name:'韩'}
- 方案2: 使用Function.protoptype.bind
function han(){
console.log(this)
}
var obj = {
name:'韩'
}
var bar = han.bind(obj)
bar() // {name:'韩'}
3. 内置函数
- 这些内置函数包括JavaScript内置函数、第三方库中的内置函数
- 内置函数要求我们传入另一个函数中
案例一: setTimeout
setTimeout(function(){
console.log(this) /// window
},1000)
setTimeOut内部是通过apply进行绑定的this对象,并且绑定的是全局对象
案例二: 数组的forEach
var names = ["abc", "cba", "nba"];
names.forEach(function(item){
console.log(this); // 三次window
})
默认情况下传入的函数是自动调用函数(默认绑定)
改变forEach this指向
names.forEach(function(item) {
console.log(this); // 三次obj对象
}, obj);
案例三:div的点击
var box = document.getElementById('app')
box.onclick = function () {
console.log(this); // box对象
}
- 在发生点击事件的时候执行传入的回调函数被调用,会将box对象绑定到该函数中。
4. new绑定
- JavaScript中的函数可以当做一个类的构造函数使用,也就是new关键字;
- 使用new关键字调用函数:
- 创建一个全新的对象;
- 这个新对象会被执行Prototype连接;
- 这个新对象会绑定到函数调用的this上(this绑定在这个时候完成);
- 如果函数没有返回其他对象,表达式会返回这个新对象;
function Persion(name) {
console.log(this); Persion{ } 空对象
this.name = name;
}
var p = new Persion("韩");
console.log(p) // Persion { name:'韩' }