this的理解
要想了解this,必须在脑子里想:this是什么?为什么要有this?this是干什么的?
- this是一个关键字,它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。
- 作为纯粹的函数调用 this指向全局对象, fn() -->window。
- 作为对象的方法调用 this指向调用对象, obj.fn() -->obj
- 作为构造函数被调用 this指向新的对象,var obj = new person() -->obj(new会改变this指向)
- apply调用 this指向apply方法的第一个参数,fn.apply() -->apply(第一个实参)
- 箭头函数中的this永远指向 该函数定义的位置所在作用域的this。
this绑定原理
01–this指向–>调用位置–默认规则
调用位置–>函数调用生效的位置
- 看 --> 带()的
- 打断点 --> debugger(打断点) --> 在控制台callstack的第二行标识该代码的调用位置(调用的栈)
明确了函数的调用位置->在该位置时的this指向
var obj = {
value: 1
}
function fn() {
// 'use strict'
console.log(this) // 2 || undefind
}
var value = 2
console.log(this)
fn()
结论:
1, 独立函数的调用 --> 默认绑定
1.1 非严格模式: this --> window
1.2 严格模式: this --> undefined
02-this指向–>隐式绑定–绑定丢失
// 如果方法调用时包含在对象的里面, 此时this->该对象
// 2.1 基本形式
function fn() {
console.log(this.value)
}
var obj = {
value: 1,
fn: fn
}
var value = 2
obj.fn()
// 2.2-> 距离fn最近的对象才是this指向
function fn() {
console.log(this.value)
}
var obj1 = {
value: 1,
fn: fn
}
var obj2 = {
value: 2,
obj1: obj1
}
var value = 3
obj2.obj1.fn()
// 2.3 -> 隐式丢失->window
function fn1() {
console.log(this.a)
}
var obj = {
a: 1,
fn1: fn1
}
var a = 2
// fn2和obj.fn1指向同一空间
var fn2 = obj.fn1
console.log(this)
fn2() // 2
// 2.4 隐式丢失->window
function fn1() {
console.log(this.a)
}
var obj = {
a: 1,
fn1: fn1
}
var a = 2
function fn2(fn) {
fn()
}
fn2(obj.fn1) // this->window
// 2.5 隐式丢失 -> this->window
function fn1() {
console.log(this.a)
}
var obj = {
a: 1,
fn1: fn1
}
var a = 2
setTimeout(obj.fn1, 1000)
结论:
obj.fn() --> this --> obj
03-this指向–>显示绑定
// 3. 显示绑定: fn.call/apply() fn.bind()() -> fn的this指向的是bind/call/apply(第一个实参)
// call/bind/apply(null||undefined) 会让显示绑定规则失效->应用默认规则->this->window
var a = 10
function fn() {
console.log(this.a); // ?
}
var obj = {
a: 20
}
// fn.call(obj)
fn.call(undefined)
结论;
fn.call(obj) -->fn里的this --> this
fn.call(undefined || null) --> 此时应用默认帮定规则 -->window
04-this指向–new绑定
// 补充
// 1. 构造函数和普通函数没啥区别
// 2. 如果函数调用使用了new关键字,此时函数为构造函数
// 3. new的作用
// 3.1 开辟空间,创造对象
// 3.2 调用方法 new person
// 3.3 new返回了创造的对象
function person(name) {
this.name = name
}
var name = 2
var obj = new person(1) // person函数中的this->obj
console.log(obj.name)
结论:
new关键字的作用:把函数中的this --> obj
05-this指向-小结-优先级
- 默认绑定 --> 独立调用函数 --> fn() --> window || undefined
- 隐式绑定 --> obj.fn() --> this–>obj
a. 回调函数 --> 容易丢失
b. var fn2 = obj.fn1 fn2() - 显示绑定 call apply bind
a. fn.call(obj) --> fn的this --> obj
b. fn.call(null || undefined) -->应用默认绑定规则 - new绑定 --> Person方法中的this --> 实例化对象
小结:
- 非箭头函数:this指向方法调用位置所在上下文(作用域)的this指向
- 箭头函数:this指向的是该函数定义(声明)的位置所在的作用域的this
// 箭头函数的使用场景? // 1. 构造函数不能用()=>{} // 2. 匿名函数通常是cb->cb会出现隐式丢失->this有问题->改为箭头函数
- 同时出现多种绑定情况:4种绑定方式的优先级:new --> 显 --> 隐 --> 默认!!!
1. 默认规则 --> 独立调用 2. 最开始执行的函数
最后练习一下吧,检验学习成果
//第一题
var num = 1;
var myObject = {
num: 2,
add: function() {
this.num = 3;
(function() {
console.log(this.num);
this.num = 4;
})();
console.log(this.num);
},
sub: function() {
console.log(this.num)
}
}
myObject.add();
console.log(myObject.num);
console.log(num);
var sub = myObject.sub;
sub();
//分析答案
var num = 1;
var myObject = {
num: 2,
add: function() {
this.num = 3; // 隐式绑定 修改 myObject.num = 3
(function() {
console.log(this.num); // 默认绑定 输出 1
this.num = 4; // 默认绑定 修改 window.num = 4
})();
console.log(this.num); // 隐式绑定 输出 3
},
sub: function() {
console.log(this.num) // 因为丢失了隐式绑定的myObject,所以使用默认绑定 输出 4
}
}
myObject.add(); // 1 3
console.log(myObject.num); // 3
console.log(num); // 4
var sub = myObject.sub;// 丢失了隐式绑定的myObject
sub(); // 4
//1、3、3、4、4
//第二题
var obj = {
say: function() {
function _say() {
console.log(this);
}
console.log(obj);
return _say.bind(obj);
}()
}
obj.say()
// 1、赋值语句是右执行的,此时会先执行右侧的对象
var obj = {
// 2、say 是立即执行函数
say: function() {
function _say() {
// 5、输出 window
console.log(this);
}
// 3、编译阶段 obj 赋值为 undefined
console.log(obj);
// 4、obj是 undefined,bind 本身是 call实现,
return _say.bind(obj);
}(),
};
obj.say();