想必大家在编程当中见过不少的
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