文章目录
- 1.this 是什么?
- 2.this的指向
- 2.1 全局上下文的 this 指向
- 2.2 函数(普通函数)上下文中的 this 指向
- 2.3 事件处理程序中的 this 指向
- 2.4 以对象的方式调用时 this 的指向
- 2.5 构造函数中的 this 指向
- 2.6 在 `类`上下文中 this 的指向。
- 2.7 `派生类`中的 this 指向
- 2.8 原型链 中的 this 指向
- 2.9 箭头函数的 this 指向
- 2.9.1定义在全局 作用域中
- 2.9.2定义在对象中
- 2.9.3定义在普通函数中
- 3. 修改 this 的指向
- call、apply 和 bind 的区别?
- 总结
1.this 是什么?
this
是一个代表指向性的关键词,他所指向的是当前整个代码执行上下文中它所属的对象
。
this
会拥有不同的值,具体取决于它所使用的位置:
- 在方法中,this 指的是
所有者对象。
- 单独的情况下,this 指的是
全局对象。
- 在函数中,this 指的是
全局对象。
- 在函数中,严格模式下,this 是
undefined
。 - 在事件中,this 指的是
接收事件的元素
。
相比于其他语言,在 JavaScript 中 函数的 this 关键字
的表现略有不同,此外,在严格模式和非严格模式
之间也会有一些差别。
在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值
,并且在每次函数被调用时 this 的值也可能会不同。ES5 引入了 bind 方法来设置函数的 this 值,而不用考虑函数如何被调用的。ES6 引入了箭头函数,箭头函数不提供自身的 this 绑定(this 的值将保持为闭合词法上下文的值)。
2.this的指向
当前执行上下文(global、function 或 eval)的一个属性,在
非严格模式下,总是指向一个对象
,在严格模式下可以是任意值。
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("实例参数")
注意
:
虽然构造函数返回的
默认值是 this 所指的那个对象
,但它仍可以手动返回其它的对象(如果返回值不是一个对象,则返回 this 对象)。
如例:
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()
注明
:
静态方法不是 this 的属性,它们只是类自身的属性。
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 的处理也有所不同,简而言之,使用箭头函数没有对 this 的绑定。在常规函数中,关键字 this 表示调用该函数的对象,可以是窗口、文档、按钮或其他任何东西。对于箭头函数,
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 的区别?
相同点:
都是用来改变函数中this的指向的。
不同点
:
(执行方式不同)
:
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
的所有使用场景。所以本章节内容还是很值得大家阅读的。
🚵♂️ 博主座右铭:向阳而生,我还在路上!
——————————————————————————————
🚴博主想说:将持续性为社区输出自己的资源,同时也见证自己的进步!
——————————————————————————————
🤼♂️ 如果都看到这了,博主希望留下你的足迹!【📂收藏!👍点赞!✍️评论!】
——————————————————————————————