this的指向始终让我们捉摸不透,用不好就会出错,所以仔细研究了一下这个,this的指向是在函数被调用时才能确定的,在定义时是不能够确定的this指向的,也就是说this指向调用它的对象。
下面内容参考这篇博文:https://www.cnblogs.com/hjson/archive/2019/01/11/10254555.html
内容有点多,总共分为三大部分!!!
第一部分:this显示绑定和隐式绑定
一. this显示绑定
含义: 当一个函数没有明确的调用对象的时候, 也就是单纯作为独立函数调用的时候, 将对函数的this使用默认绑定: 绑定到全局的window对象在显式绑定下: 函数将取得在“ 包含对象“ 里的永久居住权, 一直都会” 住在这里“。
(1) 全局函数
function fire() {
console.log(this === window); //fire此时是全局的方法,this指向window
}
fire(); // 输出true
(2) 函数内嵌套函数
function fire() {
// 我是被定义在函数内部的函数哦!
function innerFire() {
console.log(this === window); //未明确调用对象,this指向window
}
innerFire(); // 独立函数调用
}
fire(); // 输出true
我们再看下面这个例子,嵌套函数中,不同位置this的指向。fire() innerFire()两个函数都是作为独立函数调用的,所以指向的就是window。
var a = 1;
console.log(this.a); //输出1 因为此时this指向了window,因而调用1
function fire() {
var a = 2;
console.log(this.a); //输出1 因为此时this指向了window,因而调用1
function innerFire() {
var a = 3;
console.log(this.a); //输出1 因为此时this指向了window,因而调用1
}
innerFire();
}
fire(); //输出1
看到这里,有人肯定会和全局作用域和函数局部作用域混淆,我们把上面的代码去掉this,就可以区分。
var a = 1;
console.log(a); //全局作用域 输出1
function fire() {
var a = 2;
console.log(a); //fire()函数局部作用域 输出2
function innerFire() {
var a = 3;
console.log(a); //innerFire()函数局部作用域 输出3
}
innerFire();
}
fire();
(3)对象内层函数 内部函数
var a = 1;
console.log(this.a); //1 this指向全局变量window
var obj = {
a: 2,
fire: function () {
var a = 3;
console.log(this.a); //2 因为是obj.fire(),调用了fire函数,因为this指向了obj,输出了obj下的a=2
function innerFire() {
var a = 4;
console.log(this.a); //1 未明确调用对象,this指向window
}
innerFire(); //没有明确调用的对象
console.log(this.a); //2 this指向obj
}
}
obj.fire();
二.this隐式绑定
(1).隐式绑定
当函数被一个对象“ 包含” 的时候, 我们称函数的this被隐式绑定到这个对象里面, 这时候, 通过this可以直接访问所绑定的对象里面的其他属性, 比如下面的a属性在隐式绑定下: 函数和只是暂时住在“ 包含对象“ 的旅馆里面, 可能过几天就又到另一家旅馆住了。
var obj = {
a: 1,
fire: function () { //此时函数的this被隐式绑定到了对象obj
console.log(this == obj); // obj中有fire函数,因而默认this指向obj
console.log(this.a); // 1 this.a 相当于obj.a =1
}
}
obj.fire(); // 输出1
相同的方法: fire函数并不会因为它被定义在obj对象的内部和外部而有任何区别。
function fire() {
console.log(this.a)
}
var obj = {
a: 1,
fire: fire
}
obj.fire(); // 输出1
(2).动态绑定
var obj = {
a: 1, // a是定义在对象obj中的属性 1
fire: function () {
console.log(this.a)
}
}
var a = 2; // a是定义在全局环境中的变量 2
obj.fire(); //1 此时fire的指向时obj
var fireInGrobal = obj.fire; //因为fireInGrobal是全局变量,this对于obj的绑定丢失,绑定到了全局window
fireInGrobal(); // 输出 2 输出全局变量
var a = 2;
var obj = {
a: 1, // a是定义在对象obj中的属性
fire: function () {
console.log(this.a)
}
}
function otherFire(fn) { //全局函数,this绑定window
fn();
}
otherFire(obj.fire); // 输出2 this对于obj的绑定丢失,绑定到了全局this上面
第二部分:this指向
1. 全局环境作用域: this在全局环境始终指向window
console.log(this === window) // true
console.log(window.alert === this.alert) // true
var fruit = "banana"; // 定义一个全局变量,等同于window.fruit = "banana"
2.函数环境 作用域: 函数由谁调用, this就指向谁。
非严格模式:
function fn() {
console.log(this); //window
}
fn() === window; // true;window.fn(),此处默认省略window
严格模式 全局环境下, this指向window。
"use strict";//使用严格模式
this.b = "MDN";
console.log(this == window) // true
console.log(b) // "MDN"
严格模式 函数环境下, this为undefined
function fn() {
"use strict"; // 这里是严格模式
console.log(this); //window
}
fn() === undefined //true
3.对象中的方法函数调用: 指向 该方法所属的对象。
//隐式调用
var obj = {
a: 1,
fn: function () {
return this;
}
}
console.log(obj.fn() == obj); //true 函数被obj调用,指向obj
//this动态绑定
var obj = {
a: 1,
fn: function () {
return this;
}
}
console.log(obj.fn()); //1 函数被obj调用,指向obj,输出obj的a=1
var a = 2;
var newfun = obj.fn; //此时更改this指向为全局变量newfun
newfun(); //2 ,this访问全局变量a=2
4.在构造函数中: this始终指向新对象。
function Person(age, name) {
this.age = age;
this.name = name
console.log(this) // 此处 this 分别指向 Person 的实例对象 p1 p2
}
var p1 = new Person(18, 'zs')
var p2 = new Person(18, 'ww')
5.通过事件绑定的方法: this 指向 绑定事件的对象。
shijian.onclick = function () {
console.log(this); // shijian
}
6.定时器函数: 因为是异步操作, this 指向 window。
延时函数内部的回调函数的this指向全局对象window( 当然我们可以通过bind方法改变其内部函数的this指向)。
我们常见的window属性和方法有alter, document, parseInt, setTimeout, setInterval, location等等, 这些在默认的情况下是省略了window前缀的。( window.alter = alter)。
//普通定时气器
setTimeout(function () {
console.log(this); // window
}, 1000);
//定时器嵌套
function Person() {
this.age = 0;
setTimeout(function () {
console.log(this);
}, 3000);
}
var p = new Person(); //3秒后返回 window 对象
//可以改变this指向 bind()方法
unction Person() {
this.age = 0;
setTimeout((function () {
console.log(this);
}).bind(this), 3000);//这里的this指的是Person
}
var p = new Person(); //3秒后返回构造函数新生成的对象 Person{...}
7.自执行函数(匿名函数): 指向全局变量window
(function inner() {
console.log(this); //this ==> window
})();
8.箭头函数
a. 箭头函数的this是在定义函数时绑定的, 不是在执行过程中绑定的
b. 箭头函数中的this始终指向父级对象
c. 所有 call() / apply() / bind() 方法对于箭头函数来说只是传入参数, 对它的 this 毫无影响。
var obj = {
a: 1,
fn: () => {
//箭头函数中的this始终指向定义时的环境
//箭头函数中的this始终指向父级对象
console.log(this); //对象内的this调用时一般指向obj,而箭头函数在创建时就指向了obj的父级对象window
}
}
obj.fn(); //window
第三部分:更改this指向
每个Function构造函数的原型prototype, 都有方法call(), apply(), bind(),都是用来重定义 this 这个对象的!
1. call() 方法
var Person = {
name: "zhangsan",
age: 19
}
function aa(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.name);
}
aa(4, 5); //this指向window--4,5 window 空
aa.call(Person, 4, 5); //this指向Person--4,5 Person{}对象 zhangsan
2.apply() 方法
apply() 与call() 非常相似, 不同之处在于提供参数的方式, apply() 使用参数数组, 而不是参数列表。
var Person = {
name: "zhangsan",
age: 19
}
function aa(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.name);
}
aa.apply(Person, [4, 5]); //this指向Person--4,5 Person{}对象 zhangsan
3. bind() 方法
bind() 创建的是一个新的函数( 称为绑定函数), 与被调用函数有相同的函数体, 当目标函数被调用时this的值绑定到 bind() 的第一个参数上。
var Person = {
name: "zhangsan",
age: 19
}
function aa(x, y) {
console.log(x + "," + y);
console.log(this);
console.log(this.name);
}
aa.bind(Person, 4, 5); //只是更改了this指向,没有输出
aa.bind(Person, 4, 5)(); //this指向Person--4,5 Person{}对象 zhangsan
4. 存储this指向到变量中。
var oDiv1 = document.getElementById("div1");
oDiv1.onclick = function () {
var that = this; //将this储存在变量中,而且不改变定时器的指向
setTimeout(function () {
console.log(that); //注意这里是_this,而不是this-- <div id="div1">点击</div>
console.log(this); //定时器的指向没有被改变--仍然是window
}, 1000)
}