JavaScript当中的this究竟是个啥?

2019-03-26 16:19:56 浏览数 (1)

对于JS的初学者而言,JS当中的this指向很难让人摸准其脉络,经常会给你一种模糊美、朦胧美的感脚!因为this并不是固定不变的,它会根据自身所执行的环境的不同而不同。而且在开发的过程中,经常因为对this的不了解出现这样或那样的错误!所以搞定this的指向是非常非常有必要的!

与其它语言当中的this不同的是,我们JS当中的this总是指向一个对象。而具体是指向哪一个对象,则要看其运行时是基于哪一个函数的执行环境所动态绑定的。

注意:this的指向并不是函数被声明时的环境。

具体到实际开发中,this的指向大致可以分为以下几种:

1、作为普通函数调用

当你的函数不是作为对象的属性来调用时,即是我们经常说的普通函数调用。此时的this为全局对象,而JS当中的全局对象指的是window。 作为普通对象调用:

代码语言:javascript复制
//定义一个全局变量age
var age=18;
//声明一个全局函数getAge
function getAge(){
    return this.age;
}
//因为是全局环境内调用的getAget函数所以指向的对象为window
console.log(getAge());//18
//你也可以这样写
console.log(window.getAge());//18

以上代码中,getAge方法在全局window下调用,所以getAge方法内的this指向的是window。为了更好的验证这一点,咱们再来对以上代码修改如下:

代码语言:javascript复制
//声明一个全局函数getAge
function getAge(){
    //由于该函数在全局环境(window)下调用,所以this为window
    this.age=81;//为this指向的window对象添加属性age
}
getAge();//全局调用函数getAge()
console.log(age);//81
//也可以这样输出
console.log(window.age);//81

以上代码中声明了一个全局函数getAge。由于该函数在全局环境(window)下调用,所以this为window。然后通过this.age为window对象增加一个age属性。所以在调用完该函数后进行console.log(window.age)输出的结果为81。

2、函数作为对象的属性来调用:

如果函数作为对象的属性来调用,函数内的this为调用函数的对象。

代码语言:javascript复制
var obj={
    //obj属性age
    age:12,
    //obj方法getAge
    getAge:function(){
        return this.age;
    }
}
//全局属性age
var age=13;
//全局方法getAge
function getAge(){
    return this.age;
}
//在obj对象下调用getAge(),this代表的是obj
console.log(obj.getAge());//12

在全局环境下,this代表的是window。所以调取的方法为全局方法getAge。又因为getAge是在window下调用的,所以内部this指向的是window对象。最终输出结果为13

代码语言:javascript复制
var obj={
    //obj属性age
    age:12,
    //obj方法getAge
    getAge:function(){
        return this.age;
    }
}
//全局属性age
var age=13;
//全局方法getAge
function getAge(){
    return this.age;
}
//在全局环境下,this代表window,所以下面可以理解为window对象下调用全局getAge
console.log(this.getAge());//13

接下来看种丢失掉this的情况,换言之,this的指向发生改变。我们先来看下面的代码

代码语言:javascript复制
var obj={
    //obj属性age
    age:12,
    //obj方法getAge
    getAge:function(){
        return this.age;
    }
}
//全局属性age
var age=13;
//全局方法getAge
function getAge(){
    return this.age;
}
//在obj对象下调用getAge(),this代表的是obj
console.log(obj.getAge());//12
//将obj下的函数getAge赋值给fn。
var fn=obj.getAge;
//在全局环境(window) 下调用fn,this代表的是window
console.log(fn());//13

当调用obj.getAge时,getAge方法是作为obj对象的属性来调用的。输出结果为12。当将obj.getAge赋值给一个变量fn时,因为fn的调用是在全局环境下调用的,所以this指向的是window,输出结果为13。 将代码汇总如下,认真看看:

代码语言:javascript复制
var obj={
    //obj属性age
    age:12,
    //obj方法getAge
    getAge:function(){
        return this.age;
    }
}
//全局属性age
var age=13;
//全局方法getAge
function getAge(){
    return this.age;
}
//在obj对象下调用getAge(),this代表的是obj
console.log(obj.getAge());//12
//在全局环境下,this代表window,所以下面可以理解为window对象下调用全局getAge
console.log(this.getAge());//13
//将obj下的函数getAge赋值给fn。
var fn=obj.getAge;
//在全局环境(window) 下调用fn,this代表的是window
console.log(fn());//13
3、DOM对象的事件函数

DOM对象的事件函数内的this指向的是该DOM对象,看以下代码:

代码语言:javascript复制
<body>
    <div id="myDiv">点我吧!</div>
</body>
<script>
    //为id为"myDiv"增加点击事件
    document.getElementById("myDiv").onclick=function(){
        //this为div
        console.log(this.id);//myDiv
    }
</script>

在事件函数内添加一个子函数_fn,该子函数内的this指向的是window:

代码语言:javascript复制
<body>
    <div id="myDiv">点我吧!</div>
</body>
<script>
    var id="window";
    document.getElementById("myDiv").onclick=function(){
        function _fn(){
            console.log(this.id);//window
        }
        _fn();
    }
</script>

但往往我们需要的是让它指向触发事件的DOM对象,此时有一种解决方法可以作为参考:

代码语言:javascript复制
<body>
    <div id="myDiv">点我吧!</div>
</body>
<script>
    var id="window";
    document.getElementById("myDiv").onclick=function(){
        var _this=this;//保存对myDiv的引用
        function _fn(){
            console.log(_this.id);//myDiv
        }
        _fn();
    }
</script>
4、构造函数

在JS当中并没有类的概念,但是我们可以通过构造函数来创建对象,而且JS也提供了new操作符,使构造函数看起来更像是一个类! 构造函数与普通函数的异同:构造函数与普通函数的个表是一样的,它们的区别在于调取的方式。当用new操作符调用函数时,该函数为构造函数,否则为普通函数。

代码语言:javascript复制
function Box(){
    this.age=14;
}
var obj=new Box();
console.log(obj.age);//14

通过new来调用构造函数时的执行流程如下:

  • new 构造函数(),隐式执行了new Object();
  • 将构造函数的作用域给新对象(即new Object()创建出的对象),函数体内的this就代表这个新对象。
  • 执行构造函数的语句。
  • 隐式直接返回新对象。 需要注意的是构造函数如果直接返回一个对象,那么执行返回的对象就不是我们所期待的this:
代码语言:javascript复制
function Box(){
    this.age=14;
    return {
        age:16
    }
}
var obj=new Box();
console.log(obj.age);//16
5、通过call与apply改变this的指向

通过call与apply可以动态的改变函数内的this

代码语言:javascript复制
//声明一个全局变量color
var color="red";
//声明一个全局对象obj,为 obj添加一个属性color值为yellow
var obj= {
    color: "yellow"
}
//添加一个构造函数
function Fn(){
    this.color="blue";
}
//普通函数
function getColor(){
    console.log(this.color);
}
getColor();//red
getColor.call(window);//red
getColor.call(this);//red
getColor.call(obj);//yellow
getColor.call(new Fn());//blue

以上代码通过call来改变函数体内this的指向,以上代码用apply也是可以实现相同的功能。只需要将call改变apply即可:

代码语言:javascript复制
var color="red";
var obj= {
    color: "yellow"
}
function Fn(){
    this.color="blue";
}
function getColor(){
    console.log(this.color);
}
getColor();//red
getColor.apply(window);//red
getColor.apply(this);//red
getColor.apply(obj);//yellow
getColor.apply(new Fn());//blue

call与apply的用法是一样,区别仅在于传入的参数形式不同。

代码语言:javascript复制
function fn(num1,num2){
    console.log(num1 num2 num);
}
var num=5;
var obj={
    num:8
}
fn.call(this,1,2);//8
fn.call(obj,1,2);//8 
//apply 传递的参数需要用中括号进行包裹
fn.apply(this,[3,4]);//12
fn.apply(obj,[3,4]);//12

6、通过bind指定函数内部的this

代码语言:javascript复制
var age=81;
var obj={
    age:18
}
//指定fn函数体内的this指向window
var fn=function(){
    console.log(this.age);
}.bind(this);
//指定fn2函数体内的this指向obj
var fn2=function(){
    console.log(this.age);
}.bind(obj);
fn();//81
fn2();//18

好了,今天就先到这里吧!

0 人点赞