javascript中this的指向详解

在javascript中this的指向一直是前端同事的心头病,也同时是各面试题的首选,现在我们就来总结一下js中this的指向。首先需要了解一下几个概念:

1:全局变量默认挂载在window对象

2:一般情况下this指向它的调用者

3:es6的箭头函数中,this指向创建者,并非调用者

4:通过call、apply、bind可以改改变this的指向

下面我们具体分析一下

1:在函数调用时

  (非严格模式)

1
2
3
4
5
6
7
8
const func =  function  () {
     console.log( this );
     const func2 =  function  () {
       console.log( this );
     };
     func2();  //Window
   };
   func();  //Window

  (严格模式)

1
2
3
4
5
6
7
8
9
'use strict'
   const func =  function  () {
     console.log( this );
     const func2 =  function  () {
       console.log( this );
     };
     func2();  //undefined
   };
   func();  //undefined

    结合第四和第一两条规则:func这个函数是全局的,默认挂载在window对象下,this指向它的调用者即window,所以输出window对象,但是在严格模式下,this不允许指向全局变量window,所以输出为undefined(func2在函数直接调用时默认指向了全局window,其实这属于javascript设计上的缺陷,正确的设计方式是内部函数的this 应该绑定到其外层函数对应的对象上,为了规避这一设计缺陷,聪明的 JavaScript 程序员想出了变量替代的方法,约定俗成,该变量一般被命名为 that。这种方式在接下来会讲到)。

2:作为对象方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const user = {
 
     userName:  '小张' ,
     age: 18,
     selfIntroduction:  function  () {
       const str =  '我的名字是:'  this .userName +  ",年龄是:"  this .age;
       console.log(str);
 
       const loop =  function  () {
         console.log( '我的名字是:'  this .userName +  ",年龄是:"  this .age);
       };
 
       loop();    //我的名字是:undefined,年龄是:undefined
 
     }
   };
 
   user.selfIntroduction();   //我的名字是:小张,年龄是:18

    按照咱的第一条规则,this指向他的调用者,selfIntroduction()方法的调用者是user,所以在selfIntroduction()方法内部this指向了他的父对象即user,而loop方法输出的为undefined的原因就是我在上面所说的javascript的设计缺陷了,在这种情况下,我们通常选择在selfIntroduction()方法里将this缓存下来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const user = {
     userName:  '小张' ,
     age: 18,
     selfIntroduction:  function  () {
       const str =  '我的名字是:'  this .userName +  ",年龄是:"  this .age;
       console.log(str);
 
       const that= this ;
 
       const loop =  function  () {
         console.log( '我的名字是:'  + that.userName +  ",年龄是:"  + that.age);
       };
 
       loop();    //我的名字是:小张,年龄是:18
 
     }
   };
 
   user.selfIntroduction();   //我的名字是:小张,年龄是:18

此时loop的this指向就理想了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const user={
 
     userName: '小张' ,
     age:18,
     selfIntroduction: function (){
       const str= '我的名字是:' + this .userName+ ",年龄是:" + this .age;
       console.log(str);
     }
   };
 
   const other =user.selfIntroduction;
   other();  //我的名字是:undefined,年龄是:undefined
 
   const data={
     userName: '小李' ,
     age:19,
   };
   data.selfIntroduction=user.selfIntroduction;
   data.selfIntroduction();  //我的名字是:小李,年龄是:19

  在看这段代码,将selfIntroduction()赋值给了全局变量other,调用other()方法,other挂载在全局函数window对象下,window对象下没有userName 和 age 这两个属性,所以输出为undefined。第二段代码,申明了data对象,包含了username和age属性,记住我们的第二条规则一般情况下this指向它的调用者,大家就明白了,data是selfIntroduction()的函数的调用者,所以输出了data的userName和age。

3:在html里作为事件触发

1
2
3
4
5
6
7
<body>
   <p id= "btn" >点击我</p>
</body>
      const btn=document.getElementById( 'btn' );
     btn.addEventListener( 'click' , function  () {
       console.log( this );  //<p id="btn">点击我</p>
     })

在种情况其实也是遵循了第二条规则一般情况下this指向它的调用者,this指向了事件的事件源即event。

4:new关键字(构造函数


1
2
3
4
5
const fun= function (userName){
     this .userName=userName;
   }
   const user= new  fun( '郭德纲' ); 
   console.log(user.userName);  //郭德纲

这个就不多赘述了,new关键字构造了一个对象实例,赋值给了user,所以userName就成为了user对象的属性。

5:es6(箭头函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const func1=()=>{
     console.log( this );
   };
   func1();  //Window
const data={
     userName: '校长' ,
     selfIntroduction: function (){
       console.log( this );  //Object {userName: "校长", selfIntroduction: function}
       const func2=()=>{
         console.log( this );  //Object {userName: "校长", selfIntroduction: function}
       }
       func2();
     }
   }
   data.selfIntroduction();

  大家在看看我开头说的第三条准则:es6的箭头函数中,this指向创建者,并非调用者,fun1 在全局函数下创建,所以this指向全局window,而fun2在对象data下创建,this指向data对象,所以在func2函数内部this指向data对象,个人认为es6的箭头函数的this指向是对我上面所说的javascript设计缺陷的改进,(个人认知)。

6:改变this的指向

  call、apply、bind这三个函数是可以人为的改变函数的this指向的,在这里就不多说这三者的区别了,在往后的博客里我会详细解释这三者的区别的。现在先拿一个来举一个例子

1
2
3
4
5
const func= function (){
    console.log( this );
  };
  func();  //window
  func.apply({userName: "郭德纲" });  //Object {userName: "郭德纲"}

  这三个方法都是可以人为的改变this的指向,区别是call、apply会将该方法绑定this之后立即执行,而bind方法会返回一个可执行的函数。apply与call的用法是一致的,只是传参形式上call我们是这样的xxx.call(obj, arg1, arg2,...),而apply的参数必须要以数组的形式传递xxx.apply(obj, ['arg1', 'arg2', ...]),

bind是以call的形式传参的,但是bind与call、apply最大的区别就是bind绑定this指向并传参后仍然为一个函数,并没有去调用,而call与apply是直接调用函数。

说这很多总结起来就是我开头说的4点

1:全局变量默认挂载在window对象下

2:一般情况下this指向它的调用者

3:es6的箭头函数中,this指向创建者,并非调用者

4:通过call、apply、bind可以改改变this的指向

转自:http://www.php.cn/js-tutorial-375372.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值