this指向

Posted sherrycat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了this指向相关的知识,希望对你有一定的参考价值。

技术图片

 

 

 在创建执行上下文这个阶段中,最后是确定this指向。

一个非常重要一定要牢记于心的结论:this的指向,是在函数被调用的时候确定的。也就是执行上下文被创建的时候确定的。

同一个函数,调用的方式不同,this的指向也不同:

var a = 10;
var obj = 
    a: 20


function fn () 
    console.log(this.a);


fn(); // 10
fn.call(obj); // 20

B.apply(A, arguments):即A对象应用B对象的方法。

在函数执行的过程中,this的指向一旦确定,就不可被更改了。

var a = 10;
var obj = 
    a: 20


function fn () 
    this = obj; // 这句话试图修改this,运行后会报错
    console.log(this.a);


fn();

一、全局对象中的this

全局环境中的this,指向它本身。

// 通过this绑定到全局对象
this.a2 = 20;

// 通过声明绑定到变量对象,但在全局环境中,变量对象就是它自身
var a1 = 10;

// 仅仅只有赋值操作,标识符会隐式绑定到全局对象
a3 = 30;

// 输出结果会全部符合预期
console.log(a1);
console.log(a2);
console.log(a3);

二、函数中的this

在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。严格模式下,如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象;如果函数独立调用,那么该函数内部的this,则指向undefined。但是在非严格模式中,当this指向undefine‘分它是否独立调用。
要着重理解严格模式和非严格模式的区别,实际开发中基本上都是用的严格模式,ES6也是严格模式。

三、使用call、apply指定this

call和apply的功能一样,只是参数不同。call是列举出所有参数,而apply是以将参数放入数组,以数组的形式传递。举例:

function fn() 
    console.log(this.a);

var obj = 
    a: 20


fn.call(obj); 

上面输出为20。可以看到,通过call,将fn内部的this绑定为obj,因此就可以通过this.a访问obj的a属性了。

再看添加参数的例子:

function fn(num1, num2) 
    console.log(this.a + num1 + num2);

var obj = 
    a: 20


fn.call(obj, 100, 10); // 130
fn.apply(obj, [20, 10]); // 50

call和apply的第一个参数是this绑定的对象,后面就是传入fn的参数。

call/apply的应用场景

1.将类数组对象转换为数组

function exam(a, b, c, d, e) 

    // 先看看函数的自带属性 arguments 什么是样子的
    console.log(arguments);

    // 使用call/apply将arguments转换为数组, 返回结果为数组,arguments自身不会改变
    var arg = [].slice.call(arguments);

    console.log(arg);


exam(2, 8, 9, 10, 3);

// result:
//  ‘0‘: 2, ‘1‘: 8, ‘2‘: 9, ‘3‘: 10, ‘4‘: 3 
// [ 2, 8, 9, 10, 3 ]
//
// 也常常使用该方法将DOM中的nodelist转换为数组
// [].slice.call( document.getElementsByTagName(‘li‘) );

nodelist是DOM操作取出来的集合,而不是数组,不能直接用数组元素的方法来操作nodelist。

2.根据自己的需要灵活修改this指向

3.实现继承

// 定义父级的构造函数
var Person = function(name, age) 
    this.name = name;
    this.age  = age;
    this.gender = [‘man‘, ‘woman‘];


// 定义子类的构造函数
var Student = function(name, age, high) 

    // use call
    Person.call(this, name, age);
    this.high = high;

Student.prototype.message = function() 
    console.log(‘name:‘+this.name+‘, age:‘+this.age+‘, high:‘+this.high+‘, gender:‘+this.gender[0]+‘;‘);


new Student(‘xiaom‘, 12, ‘150cm‘).message();

// result
// ----------
// name:xiaom, age:12, high:150cm, gender:man;

在student的构造函数中,借助call方法,将父级的构造函数执行了一次,相当于将person中的代码在student中复制了一份,其中this的指向为从student中new出来的实例对象。

4.在向其它执行上下文的传递中,确保this的指向不变

举个栗子:

var obj = 
    a: 20,
    getA: function() 
        setTimeout(function() 
            console.log(this.a)
        , 1000)
    


obj.getA();

在这段代码中,希望的是this.a输出的是20,也就是说希望this指向obj,但是由于setTimeout中的匿名函数,所以当中的this指向丢失而指向了全局。因此可以将this的指向保存起来:

var obj = 
    a: 20,
    getA: function() 
        var self = this;
        setTimeout(function() 
            console.log(self.a)
        , 1000)
    


obj.getA()

这样的话,在obj.getA()时,self为obj,输出为20。

还可以直接利用ES5中的bind方法:

var obj = 
    a: 20,
    getA: function() 
        setTimeout(function() 
            console.log(this.a)
        .bind(this), 1000)
    


obj.getA()

在obj.getA()时,bind(this)中的this指向obj,可以将匿名函数中的this的指向改为obj。

还可以借助闭包与apply方法,封装一个bind方法(※):

function bind(fn, obj) 
    return function() 
        return fn.apply(obj, arguments);
    


var obj = 
    a: 20,
    getA: function() 
        setTimeout(bind(function() 
            console.log(this.a)
        , this), 1000)
    


obj.getA();

借助call方法实现bind(※):

if (!Function.prototype.bind) 
    Function.prototype.bind = function () 
        var self = this,                        // 保存原函数
        context = [].shift.call(arguments), // 保存需要绑定的this上下文
        args = [].slice.call(arguments);    // 剩余的参数转为数组
        return function ()                     // 返回一个新函数
            self.apply(context,[].concat.call(args, [].slice.call(arguments)));
        
    

 

四、构造函数与原型方法上的this

结合例子:

function Person(name, age) 

    // 这里的this指向了谁?
    this.name = name;
    this.age = age;   


Person.prototype.getName = function() 

    // 这里的this又指向了谁?
    return this.name;


// 上面的2个this,是同一个吗,他们是否指向了原型对象?

var p1 = new Person(‘Nick‘, 20);
p1.getName();

 总结了一篇关于原型和原型链的文章。这里的this都是指向p1。

必须要搞明白,new的过程中到底发生了什么(※)

(1)创建了一个新的对象

(2)将构造函数的this指向这个新对象

(3)指向构造函数的代码,为这个对象添加属性、方法等

(4)返回新对象

 

以上是关于this指向的主要内容,如果未能解决你的问题,请参考以下文章

Vue--记录各种this的指向

什么是this指向?怎么改变this指向

看一遍就能掌握 js 中的 this 指向

Javasript中this指向问题和改变this指向的方法

this 的指向

this 的指向