文章目录
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
的所有使用场景。所以本章节内容还是很值得大家阅读的。