ES6中新增了箭头函数这种语法,箭头函数以其简洁性和方便获取this的特性,俘获了大批粉丝儿
它也可能是面试中的宠儿, 我们关键要搞清楚 箭头函数和普通函数中的this
一针见血式总结:
普通函数中的this:
1. this总是代表它的直接调用者(js的this是执行上下文), 例如 obj.func ,那么func中的this就是obj
2.在默认情况(非严格模式下,未使用 'use strict'),没找到直接调用者,则this指的是 window (约定俗成)
3.在严格模式下,没有直接调用者的函数中的this是 undefined
4.使用call,apply,bind(ES5新增)绑定的,this指的是 绑定的对象
箭头函数中的this
箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象, 定义它的时候,可能环境是window; 箭头函数可以方便地让我们在 setTimeout ,setInterval中方便的使用this
下面通过一些例子来研究一下 this的一些使用场景
要整明白这些, 我们需要首先了解一下作用域链:
当在函数中使用一个变量的时候,首先在本函数内部查找该变量,如果找不到则找其父级函数,
最后直到window,全局变量默认挂载在window对象下
1.全局变量默认挂载在window对象下
-
<script>
-
var aa = 2;
-
alert(window.aa);
-
(function () {
-
aa = 3;
-
})();
-
alert(window.aa);
-
</script>
我们仅仅声明了一个全局变量aa,但是打印出window.aa却和aa保持一致,为什么呢?
眼见为实, 我们使用 console.dir(window) 打印 window对象看看
我们可以看到在window属性中,看到 aa 属性了;此外,函数也适用于此情况,全局函数也会挂在在window对象下
我们常见的window的属性和方法有: alert, location,document,parseInt,setTimeout,setInterval等,window的属性默认可以省略window前缀!
2.在普通函数中,this指向它的直接调用者;如果找不到直接调用者,则是window
我们来看一些例子
示例1:
-
<script>
-
function test() {
-
console.log(this);
-
}
-
test();
-
</script>
结果是: window
原因: test()是一个全局函数,也就是说是挂在window对象下的,所以 test()等价于 window.test() ,所以此时的this是window
示例2:
-
<script>
-
var obj = {
-
say: function () {
-
setTimeout(function () {
-
console.log(this)
-
});
-
}
-
}
-
obj.say();
-
</script>
结果是: window
匿名函数,定时器中的函数,由于没有默认的宿主对象,所以默认this指向window
问题: 如果想要在setTimeout/setInterval中使用这个对象的this引用呢?
用一个 变量提前把正确的 this引用保存 起来, 我们通常使用that = this, 或者 _this = this来保存我们需要的this指针!
-
<script>
-
var obj = {
-
func: function() {},
-
say: function () {
-
var that = this; //此时的this就是obj对象
-
setTimeout(function () {
-
console.log(this)
-
that.func()
-
});
-
}
-
}
-
obj.say();
-
</script>
我们也可以使用 func.bind(this) 给回调函数直接绑定宿主对象, bind绑定宿主对象后依然返回这个函数, 这是更优雅的做法
- <span style="font-family:'Times New Roman';"><script>
- var obj = {
- func: function() {},
- say: function () {
- // 此时的this就是obj对象
- setTimeout(function () {
- console.log(this)
- this.func()
- }.bind(this));
- }
- }
- obj.say(); // obj
- </script></span>
示例3(改变自360面试题):
-
window.val = 1;
-
var obj = {
-
val: 2,
-
dbl: function () {
-
this.val *= 2;
-
val *= 2;
-
console.log(val);
-
console.log(this.val);
-
}
-
};
-
// 说出下面的输出结果
-
obj.dbl();
-
var func = obj.dbl;
-
func();
结果是: 2 4 8 8
<1> 12行代码调用
val变量在没有指定对象前缀,默认从函数中找,找不到则从window中找全局变量
即 val *=2 就是 window.val *= 2
this.val默认指的是 obj.val ;因为 dbl()第一次被obj直接调用
<2>14行代码调用
func() 没有任何前缀,类似于全局函数,即 window.func调用,所以
第二次调用的时候, this指的是window, val指的是window.val
第二次的结果受第一次的影响
3.在严格模式下的this
-
<script>
-
function test() {
-
'use strict';
-
console.log(this);
-
}
-
test();
-
</script>
结果是: undefined
4.箭头函数中的 this
-
<script>
-
var obj = {
-
say: function () {
-
setTimeout(() => {
-
console.log(this)
-
});
-
}
-
}
-
obj.say(); // obj
-
</script>
此时的 this继承自obj, 指的是定义它的对象obj, 而不是 window!
示例(多层嵌套的箭头函数):
-
<script>
-
var obj = {
-
say: function () {
-
var f1 = () => {
-
console.log(this); // obj
-
setTimeout(() => {
-
console.log(this); // obj
-
})
-
}
-
f1();
-
}
-
}
-
obj.say()
-
</script>
因为f1定义时所处的函数 中的 this是指的 obj, setTimeout中的箭头函数this继承自f1, 所以不管有多层嵌套,都是 obj
示例(复杂情况: 普通函数和箭头函数混杂嵌套)
-
<script>
-
var obj = {
-
say: function () {
-
var f1 = function () {
-
console.log(this); // window, f1调用时,没有宿主对象,默认是window
-
setTimeout(() => {
-
console.log(this); // window
-
})
-
};
-
f1();
-
}
-
}
-
obj.say()
-
</script>
示例(严格模式下的混杂嵌套)
-
<script>
-
var obj = {
-
say: function () {
-
'use strict';
-
var f1 = function () {
-
console.log(this); // undefined
-
setTimeout(() => {
-
console.log(this); // undefined
-
})
-
};
-
f1();
-
}
-
}
-
obj.say()
-
</script>
结果都是undefined
关于this指向问题的讨论一直是学习js不可忽视的重要部分,那些一个又一个围绕this挖的笔试坑,仿佛永远也填不完
- var obj={
- fn:function(){
- console.log(this);
- }
- }
- obj.fn();//object
再看下面这段
- var obj={
- fn:function(){
- setTimeout(function(){
- console.log(this);
- });
- }
- }
- obj.fn();//window
假如我们在这里使用箭头函数呢
- var obj={
- fn:function(){
- setTimeout(() => {
- console.log(this);
- });
- }
- }
- obj.fn();//object
为了更加清楚的对比一般函数和箭头函数this指向的区别,我们给对象添加变量
- var obj={
- num:3,
- fn:function(){
- setTimeout(function(){
- console.log(this.num);
- });
- }
- }
- obj.fn();//undefined
- //............................................................
- var obj1={
- num:4,
- fn:function(){
- setTimeout(() => {
- console.log(this.num);
- });
- }
- }
- obj1.fn();//4
接下来看更复杂的情况
多层嵌套的箭头函数
- var obj1={
- num:4,
- fn:function(){
- var f=() => { //object,也就是指obj1
- console.log(this);
- setTimeout(() => {
- console.log(this);// //object,也就是指obj1
- });
- }
- f();
- }
- }
- obj1.fn();
- var obj1={
- num:4,
- fn:function(){
- var f=function(){
- console.log(this); //window,因为函数f定义后并没有对象调用,this直接绑定到最外层的window对象
- setTimeout(() => {
- console.log(this);//window,外层this绑定到了window,内层也相当于定义在window层(全局环境)
- });
- }
- f();
- }
- }
- obj1.fn();
- var obj1={
- num:4,
- fn:function(){
- var f=() => {
- console.log(this); //object,f()定义在obj1对象中,this就指向obj1,这就是箭头函数this指向的关键
- setTimeout(function() {
- console.log(this);//window,非箭头函数的情况下还是要看宿主对象是谁,如果没有被对象调用,函数体中的this就绑定的window上
- });
- }
- f();
- }
- }
- obj1.fn();
总结:
1.箭头函数的this绑定看的是this所在的函数定义在哪个对象下,绑定到哪个对象则this就指向哪个对象
2.如果有对象嵌套的情况,则this绑定到最近的一层对象上