文章目录

1.this 是什么?
this会拥有不同的值,具体取决于它所使用的位置:
- 在方法中,this 指的是
所有者对象。 - 单独的情况下,this 指的是
全局对象。 - 在函数中,this 指的是
全局对象。 - 在函数中,严格模式下,this 是
undefined。 - 在事件中,this 指的是
接收事件的元素。
相比于其他语言,在 JavaScript 中 函数的 this 关键字的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。
在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES6 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。
2.this的指向
2.1 全局上下文的 this 指向
在全局上下文环境中(在任何函数体外部),直接单独执行,不论开不开启严格模式,this的指向都是 全局 window 对象。
<script>
"use strict"; //开启严格模式
console.log(this); //window 对象
</script>
2.2 函数(普通函数)上下文中的 this 指向
在函数内部,this的值取决于函数被调用的方式。
如下的代码在未开启严格模式下,且 this 的值不是由该调用设置的,所以 this 的值默认指向也是全局对象 window。
<script>
function fn() {
console.log(this); //全局window 对象
}
fn()
</script>
即使函数经过 深层次嵌套,this 依然指向 全局 window 对象
<script>
function fn() {
console.log(this); //全局window 对象
function user() {
console.log(this); //全局window 对象
function type() {
console.log(this); //全局window 对象
}
type()
}
user()
}
fn()
</script>
然而,在开启严格模式下,如果进入执行环境时没有设置 this 的值,并且 JavaScript 严格模式下不允许默认绑定。因此在函数中使用时,this 是未定义的,指向 undefined。
<script>
"use strict"; //开启严格模式
function fn() {
console.log(this); // undefined
}
fn()
</script>
2.3 事件处理程序中的 this 指向
在 HTML 事件处理程序中,this 指向的是接收此事件的 HTML Dom 元素:
<button onclick="fn(this)">
按钮点击
</button>
<script>
function fn(val) {
console.log(val); // <button οnclick="fn(this)">按钮点击</button>
}
</script>

2.4 以对象的方式调用时 this 的指向
如下案例中,person 属于 fullName 函数的拥有者,所以当函数作为对象里的方法被调用时,this 指向为调用该函数的对象。
let person = {
firstName: "Bill",
lastName : "Gates",
id : 678,
fullName : function() {
console.log(this);
return this
}
};
console.log(person.fullName());

调用返回值是 person 整个对象。
2.5 构造函数中的 this 指向

当一个函数用作构造函数时(使用new关键字),它的 this 被绑定到正在构造的新对象。
function Person(name) {
this.name = name;
console.log(this); // {name:"实例参数"}
}
new Person("实例参数")

注意:
如例:
function Person(name) {
this.name = name;
return { a: 666 }
}
let type = new Person("实例参数")
console.log(type.a); //666
如上例,在调用构造函数的过程中,手动的设置了返回对象,与this绑定的默认对象被丢弃了。(这基本上使得语句“this.name = name;” 成了“僵尸”代码,但实际上这并不是真正的“僵尸”,这条语句实际还是执行了,但是对于外部没有任何影响,因此完全可以忽略它)。
2.6 在 类上下文中 this 的指向。
this 在 类 中的表现与在函数中类似,因为类本质上也是函数(构造函数),但也有一些区别和注意事项。
在类的构造函数中,this 是一个常规对象。类中所有非静态的方法都会被添加到 this 的原型中:

class Example {
constructor() {
const proto = Object.getPrototypeOf(this);
console.log(proto);
console.log(Object.getOwnPropertyNames(proto)); // ['constructor', 'first', 'second']
}
first() { }
second() { }
static third() { }
}
new Example()

注明:
2.7 派生类中的 this 指向
不像基类的构造函数,派生类的构造函数没有初始的 this 绑定。在构造函数中调用 super() 会生成一个 this 绑定。

派生类不能在调用 super() 之前返回,除非其构造函数返回的是一个对象,或者根本没有构造函数。
class Base { } //Base 为基类:
class Bad extends Base {
constructor() {
console.log(this); //此处输出会报错
super()
console.log(this); // 此处输出不会报错
}
}
new Bad();

2.8 原型链 中的 this 指向
对于在对象原型链上某处定义的方法,同样的概念也适用。如果该方法存在于一个对象的原型链上,那么 this 指向的是调用这个方法的对象,就像该方法就在这个对象上一样。
let a = {
f: function () {
console.log(this); //{a: 1, b: 4}
return this.a + this.b;
}
};
let body = Object.create(a);
body.a = 1;
body.b = 4;
console.log(body.f()); // 5
概述:
上例中,对象 body 没有属于它自己的 f 属性,它的 f 属性继承自它的原型。虽然最终是在 a 中找到 f 属性的,这并没有关系;查找过程首先从 body .f 的引用开始,所以函数中的 this 指向 body 。也就是说,因为 f 是作为 body 的方法调用的,所以它的this指向了 body。概括说就是:在原型链中,函数都会把 this 绑定到设置或获取属性的对象身上。这是 JavaScript 的原型继承中的一个有趣的特性。
2.9 箭头函数的 this 指向
简而言之:
- 箭头函数的 this
指向取决于当前箭头函数声明的环境(执行上下文)。 - 执行上下文又分为:
全局执行上下文、函数级别上下文、eval 执行上下文。 - 因为
箭头函数没有自己的 arguments 和 this,箭头函数需要获取函数定义时所在的 context 的 this,箭头函数定义在哪个作用域中,它的 this 就继承当前作用域 this 的指向。
2.9.1定义在全局 作用域中
<script>
let typefn = () => { console.log(this) } // 指向 全局window
</script>
2.9.2定义在对象中
由于箭头函数没有自己的 this,所以 它的 this 使用的是 父级作用域的 this。
var name = "关羽";
let obj = {
user: "马超",
nest: {
talk: () => {
console.log(this.name); // 指向了 window,所以打印 关羽
}
}
}
obj.nest.talk();
// {} 不会生成作用域,即箭头函数声明在全局作用域中,this 指向全局
// 通过 var 声明的变量会被认作是 window 的属性 即 var name = "关羽"; window.name == "关羽"
注意:
如上案例中,由于 name 是通过 var 关键字声明的变量,属于ES5 的方法,具有变量提升的特性,会默认成为 window 对象的属性 而箭头函数中的 this 又直接指向全局 window 对象,所以,就可以直接在 this 身上 拿到 name的 值,但是如果:name 是通过 ES6的 let关键字 声明的 ,由于 let 不会变量提升,不会绑定到 window 对象身上,所以 在箭头函数中,输出 this.name 会找不到键值对。
2.9.3定义在普通函数中
如上 同理 箭头函数没有自己的 this,所以 它的 this 使用的是 父级作用域的 this。
function fn(){
let num = ()=>{
console.log(this); // window,箭头函数没有自己的 this,它的 this 使用 fn 的 this
}
num();
}
fn();
3. 修改 this 的指向
JS 提供了3个方法 可以用来重定向 this 的指向。
call()apply()bind()
let obj = { a: 1, b: 2 }
function foo() {
console.log(this); //默认指向全局 window 对象
}
foo.call(obj); //{a: 1, b: 2} //改变指向,使其foo 的this 指向 obj
foo.apply(obj); //{a: 1, b: 2} //改变指向,使其foo 的this 指向 obj
foo.bind(obj)(); //{a: 1, b: 2} //改变指向,使其foo 的this 指向 obj
当有参数传递时候:
let obj = { a: 1, b: 2 }
function foo(val, num) {
console.log(val,num);
}
foo.call(obj, 3, 4);
foo.apply(obj, [5, 6]); //第二个参数是一个数组,数组里面的元素会被展开传入foo,作为foo的参数
foo.bind(obj)(7);
call、apply 和 bind 的区别?
相同点:
不同点:
(执行方式不同):
call 和 apply 都能立即执行函数并且改变this的指向;
bind 则是将函数返回,供后续调用。bind不改变原函数的this指向,而是创建了一个新的函数。调用方式也不同,需要使用函数名加()来调用,如:func.bind(obj)(arg1, arg2)。
(参数传递方式不同):
call 和 apply 传递参数的方式不同,call接受一个参数列表,如:func.call(obj, arg1, arg2);apply则接受一个数组数组作为参数,如:func.apply(obj, [arg1, arg2])。
bind 参数传递 和 call 是一样的。
在函数中的形参,会把相应的参数,进行展开接收。
总结
以上就是本章节,给大家带来的 JavaScript 中 this 关键字的 作用以及使用方法,this 这个知识点,还是比较重要的,本章的知识点内容,略微有点多,但是知识点,博主整理的几乎涵盖了 this 的所有使用场景。所以本章节内容还是很值得大家阅读的。










