this作用全解(全局this 、函数this、全局函数 this call apply bind……)
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
this 是什么
老是记不住 this 指向谁我觉得这是非常正常的现象因为 this 指向的情况本身就比较多面对这种不太好记的知识点我们可以延续之前的学习方式采用敲代码实验 + 画图记忆 + 真题训练的方式这种学习方式学知识更高效一点。
首先学习一个知识先要理解它是什么在大多数面向对象语言中this 表示当前对象的一个引用而 JS 中的 this 是完全不同的概念。
我们查阅 MDN 文档可知this 是当前执行上下文global、function 或 eval的一个属性。
也就是说我们可以把 JS 中的 this 分为三种分别是
- 全局上下文中的 this。
- 函数上下文中的 this。
- eval 上下文中的 this。
关于 eval 在 MDN 或很多书籍和文档中都建议永远不要使用它我们就不讨论它了接下来我们将主要讨论全局上下文和函数上下文的 this。
有一点需要注意的是node 环境中的 this 和 web 环境中的 this 是不同的为了避免大家一开始接受太多概念我们先只讨论 web 环境的 this。
接下来我们就通过做实验的方式来学习一下 this 的指向。
全局上下文的 this
全局上下文的 this 指向非常明确不管有没有启用严格模式都指向 window 对象。可以打开右侧的实验环境我们敲代码来验证一下。随便新建一个 test.html 文件在文件中写入如下代码
<body>
<script>
console.log(this === window); // 输出 true
("use strict");
console.log(this === window); // 输出 true
</script>
</body>
然后可以启动一下环境中的 web server查看代码输出情况可以看到的确是不管有没有启用严格模式全局上下文的 this 都指向 window 对象。
给 this 添加属性就相当于给 window 添加属性给window 添加属性就相当于给 this 添加属性如下代码所示
this.userName = "zhangsan";
window.age = 18;
console.log(this.age); // 输出 18
console.log(window.userName); // 输出 'zhangsan'
函数上下文的 this
全局上下文的 this 很简单记住无脑指向 window 就完事复杂就复杂在函数上下文的 this函数上下文中的 this 与 arguments 一样就是函数的隐式参数可以在任意函数中调用它的指向不是固定不变的取决于函数处于什么位置、以什么方式调用可以总结成如下图
全局上下文中的函数
直接调用全局上下文中的函数this 指向默认情况下为 window。
function fn() {
console.log(this); // 输出 window
}
fn();
function fn() {
let a = 1;
console.log(this.a); // 输出 2
}
var a = 2;
fn();
但是在严格模式下this 指向的就是 undefined。
function fn() {
"use strict";
console.log(this); // 输出 undefined
}
fn();
对象中的函数
调用对象中的函数this 指向为这个对象。
const obj = {
a: 1,
fn() {
console.log("this :>> ", this); // 输出 obj
console.log("this.a :>> ", this.a); // 输出 1
},
};
obj.fn();
但是如果函数嵌套有函数此时的 this 指向为 window就非常令人迷惑。
const obj = {
a: 1,
fn() {
return function () {
console.log("this :>> ", this); // 输出 window
console.log("this.a :>> ", this.a); // 输出 100
};
},
};
var a = 100;
obj.fn()();
其实可以这么理解
obj.fn()();
等价于;
const temp = obj.fn(); // 定义一个临时变量来存储 obj.fn 返回的函数
temp(); // 执行这个函数
箭头函数
遇到对象里有函数嵌套函数的情况想要 this 指向这个对象es6 之前可以用一个临时变量 _this 来暂存 this代码如下
const obj = {
a: 1,
fn() {
const _this = this;
return function () {
console.log("this :>> ", _this); // 输出 obj
console.log("this.a :>> ", _this.a); // 输出 1
};
},
};
obj.fn()();
es6 推出了箭头函数的概念之后我们就可以使用箭头函数解决这个问题代码如下
const obj = {
a: 1,
fn() {
return () => {
console.log("this :>> ", this); // 输出 obj
console.log("this.a :>> ", this.a); // 输出 1
};
},
};
obj.fn()();
对于普通函数来说内部的 this 指向函数运行时所在的对象。
对于箭头函数它不会创建自己的 this它只会从自己的作用域链的上一层继承 this。
所以这里 fn 中嵌套的匿名箭头函数中的 this指向它作用域链的上一层的 this也就是函数 fn 的 this也就是 obj。
其实箭头函数内部实现也是定义临时变量 _this 来暂存 this用 babel 一试便知如下图
构造函数
构造函数内如果返回值是一个对象this 指向这个对象否则 this 指向新建的实例。
function Person(name) {
this.name = name;
}
const p = new Person("zhangsan");
console.log(p.name); // 'zhangsan'
function Person(name) {
this.name = name;
return {
name: "xxx",
};
}
const p = new Person("zhangsan");
console.log(p.name); // 'xxx'
function Person(name) {
this.name = name;
return {};
}
const p = new Person("zhangsan");
console.log(p.name); // 'undefined'
如果有返回值但是不是一个对象this 还是指向实例。
function Person(name) {
this.name = name;
return 123;
}
const p = new Person("zhangsan");
console.log(p.name); // 'zhangsan'
显式改变函数上下文的 this
call
Function.prototype.call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
function fn() {
console.log(this.name);
}
const obj = {
name: "zhangsan",
};
fn.call(obj); // 指定 this 为 obj输出 'zhangsan'
apply
Function.prototype.apply() 方法调用一个具有给定 this 值的函数以及以一个数组或类数组对象的形式提供的参数。
apply 和 call 的功能完全一样只是传参形式不一样call 是传多个参数apply 是只传参数集合。
function add(x, y, z) {
return this.x + this.y + this.z;
}
const obj = {
x: 1,
y: 2,
z: 3,
};
console.log(add.call(obj, 1, 2, 3)); // 输出 6
console.log(add.apply(obj, [1, 2, 3])); // 输出 6只是传参形式不同而已
bind
Function.prototype.bind() 方法创建一个新的函数在 bind() 被调用时这个新函数的 this 被指定为 bind() 的第一个参数而其余参数将作为新函数的参数供调用时使用。
bind 和 call、apply 的区别是函数调用 call 和 apply 会直接调用而调用 bind 是创建一个新的函数必须手动去再调用一次才会生效。
function add(x, y, z) {
return this.x + this.y + this.z;
}
const obj = {
x: 1,
y: 2,
z: 3,
};
const add1 = add.bind(obj, 1, 2, 3); // bind 会返回一个新的函数
console.log(add1()); // 执行新的函数输出 6