想必大家在编程当中见过不少的
this,对this的指向也是非常的头疼,所以在这里我总结了几种常见的情况,希望对各位读者有所帮助😄
全局上下文的this
全局上下文,也就是this在全局作用域下,此时的this相当于window
conso.log(this)
// 此时在控制台中打印的结果是 window
if (this === window) {
console.log('yes')
} else {
console.log('no')
}
// 这时候打印的也是符合预期的 yes
为了让大家有更深刻的理解,这里再次做一个测试
this.userName = "zhangsan";
window.age = 18;
console.log(this.age); // 输出 18
console.log(window.userName); // 输出 'zhangsan'
这是因为给 this 添加属性,就相当于给 window 添加属性,给 window 添加属性,就相当于给 this 添加属性,也再一次验证了this在全局作用域下相当于window
全局上下文中的函数
函数上下文中的 this 与 arguments 一样,就是函数的隐式参数,可以在任意函数中调用,它的指向不是固定不变的,取决于函数处于什么位置、以什么方式调用
直接调用函数
function fn1() {
console.log(this);
}
fn1();
// 调用的结果会输出 window
// 此时函数的调用相当于 window.fn1();
function fn2() {
let a = 1;
console.log(this.a); // 输出 2
}
var a = 2;
fn2();
// 此时函数的调用相当于 window.fn2();
注意:利用
var声明的变量会有变量提升
总结:
函数当中的this指向的是函数的调用者,在全局上下文当中的函数的调用者实际是window。由于是在全局上下文当中的函数,所以调用函数时可以省略window,也就是我们通常所见到的函数名 ();形式的函数调用方式。
在严格模式下全局上下文中的函数
function fn() {
"use strict"; // 代表开启了严格模式,此时代码的检查会变得非常严格
console.log(this); // 输出 undefined
}
fn();
对象中的函数
对象当中的函数也称之为对象的方法,而方法的调用者也就是这个方法的拥有者,也就是对象
全局上下文中的对象
const obj = {
age: 18,
fn() {
console.log("this是:", this); // 输出 this是:obj
console.log("this.age是:", this.age); // 输出 this.age是:18
},
};
obj.fn();
此时对像当中的函数的 this 指的依旧是函数的调用者,也就是 fn(); 的调用者,而对象调用了这个函数 obj.fn();
所以此时 this === obj
const obj = {
age: 18,
fn() {
console.log('this是:', this) // 输出 this是:obj
console.log('this.age是:', this.age) // 输出 this.age是:18
if (this === obj) {
console.log('yes')
} else {
console.log('no')
}
}
}
obj.fn()
所得结果符合预期。
函数嵌套有函数
const obj = {
age: 18,
fn() {
return function () {
console.log("this是:", this); // 输出 this是:window
console.log("this.age是:", this.age); // 输出 this.age是:100
};
},
};
var age = 100;
obj.fn()();
其实我们可以这样理解
obj.fn()();
// 等价于
const temp = obj.fn(); // 定义一个临时变量来存储 obj.fn 返回的函数
temp(); // 执行这个函数
其实单独的
obj.fn();也不会报错,但是返回的不再是原始类型的数据了,而返回的是一个引用类型的数据 ,在这里返回的其实是一个函数,而this是在返回的函数里面使用的,那么this指向的是返回的函数的调用者。
函数嵌套有箭头函数
但是在有时候我们就想在函数嵌套有函数的情况下让
this所指向的就是obj,那么可以使用箭头函数
const obj = {
age: 18,
fn() {
return () => {
console.log("this是:", this); // 输出 this是:obj
console.log("this.age是:", this.age); // 输出 this.age是:18
};
},
};
obj.fn()();
对于普通函数来说,内部的 this 指向函数运行时所在的对象。
对于箭头函数,它不会创建自己的 this,它只会从自己的作用域链的上一层继承 this。
所以这里 fn 中嵌套的匿名箭头函数中的 this,指向它作用域链的上一层的 this,也就是函数 fn 的 this,也就是 obj。
构造函数
this 指向新建的实例
function Person(name) {
this.name = name;
}
const p = new Person("zhangsan");
console.log(p.name); // 'zhangsan'
这里的
this指向的是利用Person这个构造函数new出来的实例,也就是p。代码执行后,
this.name = name;也就意味着 为实例p当中的name属性赋值为zhangsan
构造函数内的返回值是一个对象
示例代码:
function Person(name) {
this.name = name;
return {
name: "syh",
};
}
const p = new Person("zhangsan");
console.log(p.name); // 'syh'
这里我们先打印一下 p 看看结果
到这里,我们先看看 new Person();究竟做了什么。
第一步: 创建一个Object对象实例。
第二步: 将构造函数的执行对象(也就是构造函数当中的所有代码)赋给新生成的这个实例。
第三步: 执行构造函数中的代码(构造函数当中的代码可能会给实例添加属性或者方法)
第四步: 将执行的结果返回给新生成的对象实例
就结果而言,只要
new 构造函数();就会自动的返回一个已经处理好的对象
在示例代码当中Person这个构造函数出现了return {name: "syh"};返回的是一个人为设置的对象,那么就会将那个自动生成的对象给覆盖掉,这也就是为什么 p = {name: "syh"}; 而不是 p = {name: "zhangsan"}; 的原因。所以最后打印的就是 {name: "syh"} 对象当中 name 属性所对应的值,也就是 syh
简单总结: 如果构造函数当中有 return 且
return 复杂类型;(对象也是复杂类型的一种) 则new 构造函数();返回的是那个手动设置的那个复杂类型。
return 返回的是一个原始值(简单类型)
function Person(name) {
this.name = name;
return 123;
}
const p = new Person("zhangsan");
console.log(p.name); // 'zhangsan'
当return 简单类型; 的时候,this依旧指向的是那个自动生成的对象
显式改变函数上下文的 this
call()
Function.prototype.call() ,这里出现了Function.prototype ,那么就意味着只要是函数,那么基本上都可以使用 call(); 这个方法。Function 可以简单的理解为所有函数的父级函数,prototype 指向原型对象,那么连起来就是Function.prototype.call(); 为Function这个函数的原型上挂载一个call方法。
示例代码:
function fn() {
console.log(this.name)
}
const obj = {
name: 'zhangsan'
}
var name = '我是 window 下的 name'
fn() // 输出 '我是 window 下的 name'
fn.call(obj) // 指定 this 为 obj,输出 'zhangsan'
调用
fn.call(obj);改变了原先的this指向 ,将this指向手动的修改为了obj并且调用了fn函数
apply()
Function.prototype.apply() 那么就意味着只要是函数,那么基本上都可以使用 apply(); 这个方法。
按作用效果来说的话,apply() 方法与 call() 方法一样,都会改变函数的this 指向并调用函数。区别只是只是传参形式不一样,call 是传多个参数,apply 是只传参数集合
示例代码:
function add(x, y, z) {
let result = this.x + this.y + this.z
console.log(result);
}
const obj = {
x: 1,
y: 2,
z: 3
}
add.call(obj, 1, 2, 3) // 输出 6
add.apply(obj, [1, 2, 3]) // 输出 6,只是传参形式不同而已,传入的参数是一个数组,也就是数据的集合
bind()
Function.prototype.bind() 那么就意味着只要是函数,那么基本上都可以使用 bind(); 这个方法。
如果函数调用了 bind() 方法,那么就会创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。也就意味着 函数1.bind() ,这个函数1并不会被自动调用。
function add(x, y, z) {
let result = this.x + this.y + this.z
console.log(result)
}
const obj = {
x: 1,
y: 2,
z: 3
}
add() // undefined 之间相互运算,所得结果是 NaN
const add1 = add.bind(obj) // bind 会返回一个新的函数
add1() // 执行新的函数,输出 6