zoukankan      html  css  js  c++  java
  • 理解JavaScript中的this

    这里来个倒叙,先说一下this的几种情况指向:

    • 形如obj.fn()的this指向obj,形如fn()的this指向window
    • 匿名函数的this指向window
    • 事件回调函数的this指向dom元素
    • 定时器的回调函数指向window
    • 构造函数中的this指向实例
    • 可以使用call、apply、bind来改变this的指向

    下面开始:

    我自己结合网上的定义,给出了this的定义:

    函数中this指向了执行时,调用它的并且离它最近的对象

    不过这个定义只适用于大部分情况:

    先看一段代码:

    function fn() {
        console.log(this);  
    }
    fn();//window

    如果你知道在全局作用域中定义函数变量和函数实际是在window变量上添加属性和方法的话,那么上面的代码就很好理解了,上面的代码相当于:

    function fn() {
        console.log(this);  
    }
    window.fn();//window

    最后调用这个函数的对象是window对象,所以this就指向了window,再看:

    var obj = {
        fn: function() {
            console.log(this);
        }
    }
    obj.fn(); //this指向obj,相当于window.obj.fn();

    上面的代码我们可以看到是window的obj对象调用了fn这个函数,所以定义的时候我们强调是离它最近的调用对象,这里obj离得近,this就指向了obj。

    定义时还强调了是执行时,是什么意思呢?接着看代码:

    function fn() {
        console.log(this)
    }
    var obj = {
        fn: fn
    }
    obj.fn(); //this指向obj

    定义时,函数是在全局中定义的,但是执行时我们是利用obj来调用,它就指向了obj,如果还不太确定,我们再看:

    var obj = {
        fn: function() {
            console.log(this)
        }
    }
    var fn = obj.fn;
    fn(); //this指向了window

    这下相信了吧,下面介绍几种比较特殊的情况:

    匿名函数中的this

    匿名函数具有全局性,所以this指向的是window。

    (function(){
        console.log(this); //window
    })()
    var obj = {
        fn: function() {
            (function(){
                console.log(this);
            })()
        }
    }
    obj.fn(); //window

    上面第二段代码中,尽管是obj调用了fn函数,但是在fn函数中的匿名函数仍然具有全局性,所以this仍然指向window。

    函数普通调用的this

    普通调用是什么意思呢?就是形如:fn();不是作为某个对象的方法。上面有段代码:

    var obj = {
        fn: function() {
            console.log(this);
        }
    }
    obj.fn(); //this指向obj

    我们稍微修改一下:

    var obj = {
        fn: function() {
            function innerFn() {
                console.log(this);
            }
            innerFn();
        }
    }
    obj.fn(); //输出window

    是不是有些懵逼了,尽管innerFn是在obj的fn函数中被调用的,但是它的作用域链上活动对象只有innerFn和全局本身(ES6以前,JavaScript作用域只有函数域),我猜测,在利用obj.fn()调用的时候,JavaScript内部是做了this指向处理的,而普通调用就指向了全局。

    有人可能会问,如果我要调用外层中的this怎么办?通常我们会使用一个变量来保存this,例如:

    var obj = {
        fn: function() {
            var self = this;
            function innerFn() {
                console.log(self);
            }
            innerFn();
        }
    }
    obj.fn(); //输出obj

    定时器回调函数中的this

    setTimeout(function(){
        console.log(this);  //window
    }, 1000)
    var obj = {
        fn: function() {
            console.log(this);
        }
    }
    setTimeout(obj.fn, 1000) //输出window

    setTimeout回调函数你可以看做是下面这样:

    var obj = {
        fn: function() {
            console.log(this);
        }
    }
    var callback = obj.fn;
    //在设定时间后执行回调函数
    callback();

    上面你可能就很熟悉了,调用fn函数的是全局对象,所以指向了window对象,如果你想改变定时器中函数this的指向,可以使用bind函数:

    var obj = {
        fn: function() {
            console.log(this);
        }
    }
    setTimeout(obj.fn.bind(obj), 1000) //输出obj

    事件处理函数

    在JavaScript中我们可以这样绑定一个事件:

    <div id="div">这是一个div元素</div>
    function doClickDiv(e) {
        //to do something
        console.log(this);
    }
    var oDiv = document.getElementById('div');
    //绑定点击事件
    oDiv.addEventListener('click', doClickDiv, false);

    当点击时,输出时this指向了div这个节点,早期绑定事件的写法可以帮助我们理解:

    function doClickDiv(e) {
        //to do something
        console.log(this);
    }
    var oDiv = document.getElementById('div');
    //绑定点击事件
    oDiv.onclick = doClickDiv;

    当点击div时,会触发oDiv.onclick函数,相当于oDiv.onclick(),这和使用对象调用是一样的。

    构造函数中的this

    JavaScript中的函数是可以作为构造函数的,使用new即可,那么this在这种情况下指向是什么?

    function Person() {
        this.age = 18;
        this.job = 'student';
    }
    var person = new Person();
    console.log(person)//{age: 18, job: 'student'}

    我们可以知道person是一个实例,所以函数作为构造函数时,this是指向实例的,在构造函数中实际是这样的:

    function Person() {
        //隐藏着的语句
        //this = {} 这里只是简单说明this是一个对象,它还要关联Person函数的原型
        this.age = 18;
        this.job = 'student';
        //隐藏着的语句
        //return this;
    }
    var person = new Person();
    console.log(person)//{age: 18, job: 'student'}

    从上面我们可以看出,构造函数隐式return了this,所以person就是this,但是当构造函数有return语句时,this并不一定指向person。

    当return返回一个对象时:

    function Person() {
        this.age = 18;
        this.job = 'student';
        return {
            tip: 'this is an object'
        }
    }
    var person = new Person();
    console.log(person)//{tip: "this is an object"}

    当return回一个非对象值时:

    function Person() {
        this.age = 18;
        this.job = 'student';
        return 1;
    }
    var person = new Person();
    console.log(person)//{age: 18, job: 'student'}

    call、apply、bind改变this指向

    有时我们需要改变this的指向,就可以通过这三个方法函数来实现:

    call和apply:

    var prop = 'window';
    
    function fn() {
        console.log(this.prop);
    }
    
    var obj1 = {
        prop: 'obj1',
        fn: function(){
            console.log(this.prop);
        }
    }
    var obj2 = {
        prop: 'obj2',
        fn: function(){
            console.log(this.prop);
        }
    }
    
    fn();                // 'window'
    obj1.fn();           // 'obj1'
    obj2.fn();           // 'obj2'
    fn.call(obj1)        // 'obj1'
    fn.apply(obj1)       // 'obj1'
    obj1.fn.call(obj2);  // 'obj2'
    obj1.fn.apply(obj2); // 'obj2'

    从改变this指向来说,call和apply是一样的,它们两个函数的区别在于传参数形式的不同:

    var obj = {
        a: 1,
        b: 2,
        c: 3
    }
    
    function add(a, b, c) {
        console.log(this);
        return a + b + c;
    }
    
    add.call(obj, obj.a, obj. b, obj.c);
    add.apply(obj, [obj.a, obj.b, obj.c]);

    可以清楚的看出:call接受的参数是一个一个传进去的,而apply是传一个参数数组进去的。这里插播一个问题:为什么要有call、apply同时存在?其实,有时候改变参数的形式是很有必要,下面分享一个小技巧,当你要求一个数组的最大值时,你会怎么做?传统的做法是用for遍历一遍数组,然后挑出最大值,但是利用apply你就可以直接利用js的内置函数:

    var arr = [3, 4, 2, 78];
    var max = Math.max.apply(Math, arr);  //Math.max的用法是Math.max(1, 3, 6, 2)  //6
    console.log(max); //78

    bind:

    bind函数的作用是绑定参数,其中第一个参数就是传入this指向对象,当时undefined或者null时,this指向window,它会返回一个新函数,看代码;

    function add(a,b,c){
        return a + b + c;
    }
    
    add(1,2,3) //6
    
    var addOne = add.bind(null, 1);
    
    addOne(2, 3) //6,相当于add(1, 2, 3);
    

    bind和call的区别在于call是参入参数并执行函数,而bind是传入绑定参数,返回一个新函数。

  • 相关阅读:
    VOA 2009/11/02 DEVELOPMENT REPORT In Kenya, a Better Life Through Mobile Money
    2009.11.26教育报道在美留学生数量创历史新高
    Java中如何实现Tree的数据结构算法
    The Python Tutorial
    VOA HEALTH REPORT Debate Over New Guidelines for Breast Cancer Screening
    VOA ECONOMICS REPORT Nearly Half of US Jobs Now Held by Women
    VOA ECONOMICS REPORT Junior Achievement Marks 90 Years of Business Education
    VOA 2009/11/07 IN THE NEWS A Second Term for Karzai; US Jobless Rate at 10.2%
    Ant入门
    Python 与系统管理
  • 原文地址:https://www.cnblogs.com/kang-xjtu/p/5800932.html
Copyright © 2011-2022 走看看