欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

this指向的深入解析

程序员文章站 2022-07-14 14:27:46
...

在面试题中,老是喜欢给一段代码,然后问这里的this的到底指向谁,切记这里有一个坑,有些出题人出的题目原本就是错的,然后故意问你这里的this指向的是谁。
这里要明确指出,在函数定义的时候用到了this,这时是确定不了this的指向的,只有在函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象,也可以说是函数运行时所在的环境对象。

5种this的常用方法

1. 纯粹的函数调用

这时全局性调用,其实在全局中定义的变量name和函数sayName( )都是挂载在全局对象window下面的

var name = 'mr3x';
function sayName(){
    var name = 'bill';
    console.log(this.name);
}
console.log(name);  //mr3x
console.log(window.name);  //mr3x
sayName();  //mr3x
window.sayName();  //mr3x

要注意严格模式的this,使用use strict有两种方式,一种是在脚本第一行写,作用于当前的脚本文件,一种是写在函数里面,作用于当前函数

function foo(){
    console.log(this);
}
function bar(){
    'use strict';  //使用严格模式
    console.log(this);
}
foo();  //window
bar();  //undefined

2.对象的调用

函数作为某个对象的方法给调用的时候,指向的是上级对象

function sayName(){
    console.log(this.name);
}
var person = {
    name: 'mr3x',
    sayName: sayName,
};
person.sayName();  //mr3x

下面这种情况和上面这种情况不是同一种情况,这里是将man指向了person.sayName这一个内存空间地址,还没有调用,真正的调用是man( ),然后这里又回到了纯粹的函数调用

var name = 'bill'
function sayName() {
    console.log(this.name);
}
var person = {
    name: 'mr3x',
    sayName: sayName,
};
var man = person.sayName;
man();  //bill

切记,这里是指向的上级对象,看下面的例子,

var person = {
    name: 'mr3x',
    method: {
        name: 'bill',
        sayName: function(){
            console.log(this.name);  //bill
            console.log(this);  //person.method
        }
    }
};

person.method.sayName();

尽管这个函数是给最外层的对象所调用,但是this指向也只是指向它的上级对象

3.构造函数的调用

构造函数就是通过这个函数生成一个新的对象。然后这个时候的this就指向这个新的对象

var name = 'bill'
function Person(){
    this.name = 'mr3x';
}
var obj = new Person();
console.log(obj.name);  //mr3x

下面的是特殊情况,一般不这么写,但面试题有时就是喜欢出些乱七八糟的。当遇到关键字return的时候
我们先是return一个空对象的时候,返回的是undefined

var name = 'bill'
function Person(){
    this.name = 'mr3x';
    return {}
}
var obj = new Person();
console.log(obj.name);  //undefined

那我们return 一个数值的时候,返回的竟然是mr3x

var name = 'bill'
function Person(){
    this.name = 'mr3x';
    return 1;
}
var obj = new Person();
console.log(obj.name);  //mr3x

那我们return 一个undefined的时候,返回的竟然是mr3x

var name = 'bill'
function Person(){
    this.name = 'mr3x';
    return undefined;
}
var obj = new Person();
console.log(obj.name);  //mr3x

在说为什么之前,我们先说一下null和undefined的一些基础知识

typeof null  //object
typeof undefined  //undefined
null == undefined  //true
null === undefined  //false

其实,如果return回去的是一个对象(如函数),那么this就指向这个对象,如果不是对象那么就指向new 出来的实例
但还有一个特殊的家伙就是null,这货虽然是对象,可他还是指向new 出来的实例

var name = 'bill'
function Person(){
    this.name = 'mr3x';
    return null;
}
var obj = new Person();
console.log(obj.name);  //mr3x

4.定时器的情况

setInterval和setTimeout中的回调函数中的this指向的是window

var name = 'mr3x'
var person = {
    name: 'bill',
    sayName: function(){
        setInterval(function(){
            console.log(this.name);
        }, 1000);
    }
}
person.sayName();  //mr3x

5.事件绑定的情况

这个时候this指向绑定事件的对象

<body>
    <button id="btn">click me</button>
<script>
    var btn = document.querySelector('#btn');
    btn.onclick = function(){
        console.log(this);  //button#btn
    }
</script>
</body>

3种修改this指向的方法

1.call()方法

var obj = {
    name: 'mr3x'
}
function sayName(a, b){
    var name = 'bill';
    console.log(a, b);  // 10  20
    console.log(this.name);  // mr3x
}
sayName.call(obj, 10, 20);  

sayName.call(obj, 10, 20)这句的意思是把sayName中的this指向obj,后面的10和20是要传入的参数

2.apply()方法

call()方法和apply()方法本质上没有什么区别,只是第二个参数传入的时候不一样而已,apply的参数传入是用数组的

var obj = {
    name: 'mr3x'
}
function sayName(a, b){
    var name = 'bill';
    console.log(a, b);  //10 20
    console.log(this.name);  //mr3x
}
sayName.apply(obj, [10,20]);

3.bind()方法

bind()方法返回的是一个新的函数,也叫做绑定函数,与被调用的函数拥有相同的函数体。

var obj = {
    name: 'mr3x'
}
function sayName(a, b){
    var name = 'bill';
    console.log(a, b);  //10 20
    console.log(this.name);  //mr3x
}
sayName.bind(obj, 10, 20)();
sayName.bind(obj)(10, 20);//这两种写法都可以