美文网首页
搞懂this、call、apply

搞懂this、call、apply

作者: bby365 | 来源:发表于2018-06-23 21:21 被阅读0次

    this

    this 指向的是一个对象,只用当函数执行时才能确定this指向的对象,与函数声明的环境无关。

    • this 指向
      有4中情况:对象的方法调用、普通函数调用、构造函数实例化、call和apply

    1.对象的方法调用

    //指向对象本身
        var obj = {
            name: 'bby',
            getName: function(){
                return this.name;
            }
        };
        obj.getName() // bby
    
    1. 普通函数调用
    // 一般指向全局对象,window
    window.name = 'globalName';
    var getName = function(){
        return this.name;
    };
    console.log( getName() ); // globalName
    

    当调用一个对象的方法时,this本来是指向该对象,怎样修改可以指向 window?

    window.name = 'globalName';
    var myObject = {
        name: 'sven',
        getName: function(){
            return this.name;
        }
    };
    
    var getName = myObject.getName;
    console.log( getName() ); // globalName
    

    在全局域中定义个变量,指向对象的方法引用,最后在执行个变量,此时this指向window。
    思考:怎么修改上面的代码,this还是指向myObject ?这个其实要求访问私有变量,可以使用闭包,返回一个函数:

    window.name = 'globalName';
    
    var myObject = {
        name: 'bby',
        getName: function(){
            var _this = this;
            return function(){
                console.log(_this.name)
            }
        }
    };
    
    var getName = myObject.getName();
    console.log( getName() ); // bby   
    

    接下来还有一个例子,不是很好理解,直接看代码:

    <html>
        <body>
            <div id="div1">我是一个div</div>
        </body>
        <script>
        window.id = 'window';
        document.getElementById( 'div1' ).onclick = function(){
            alert ( this.id ); // 输出:'div1'
            var callback = function(){
                alert ( this.id ); // 输出:'window'
            }
            callback();
        };
        </script>
    </html>
    

    点击事件的this指向的是点击的对象,既然 callback 是在绑定的函数中定义的,那this应该也是指向#div1对象?
    这样理解是错的,因为this的指向与函数声明环境无关,只有函数执行环境有关,问题又来了,callback是在绑定函数中执行的,按理说 此时的 this 应该指向 #div1 对象?但实际上指向的是window,为什么?
    好吧,我暂时也没有想清楚,不过怎么修改,到时会的,用变量把this保存下来。

    <html>
        <body>
            <div id="div1">我是一个div</div>
        </body>
        <script>
        window.id = 'window';
        document.getElementById( 'div1' ).onclick = function(){
            var that = this; // (1) 保存div 的引用
            var callback = function(){
                alert ( that.id ); // (2) 输出:'div1'
            }
            callback();// (3)
        };
        </script>
    </html>
    

    改变(1)(2)(3) 处代码的顺序,可以得到不同的结果,这个与预解析有关。

    1. 构造函数的调用
      this 指向的就是实例对象,但是需要理解new操作的实际过程,简单理解分为以下4步:

    1.创建一个对象obj ; 2. obj.proto 指向函数的原型对象;3. 指向构造函数,并将this指向obj ; 4.判读执行返回的结果,如果是个对象,就返回这个对象,不是的话,返回obj对象

    看下面这个例子,显示地返回对象:

        var MyClass = function(){
            this.name = 'bby';
            return { // 显式地返回一个对象
                name: '365'
            }
        };
        var obj = new MyClass();
        alert ( obj.name ); // 输出:365
    
    1. Function.prototype.call 或Function.prototype.apply 调用

    可以动态改变传入函数的this


    • 丢失 this

    写代码过程中,经常遇到this指向不明确的,原因是一开始我们就忽略了this的指向。
    比如:封装document.getElementById(),一般都会习惯这样写:

    var getId = function( id ){
        return document.getElementById( id );
    };
    getId( 'div1' );
    

    有没有想过,下面这样的写法:

    var getId = document.getElementById;
    getId( 'div1' );
    

    这种写法会报错,因为getId 和document.getElementById 指向同一个引用,document.getElementById方法内部现实时需要用到this,且this要指向document。但是直接用上面的方法,执行getId() , this 指向的是window 。
    所以可以这样修改:

    var getId = document.getElementById;
    getId.apply(document, ['div1'] );
    

    call 和 apply

    call 和 apply 比较常用,基本区别就是传参的形式不同。

    • call 和 apply 的用途
      有三种情况:一是改变this指向;二是封装Function.prototype.bind();三是借用其他对象的方法。
    1. 改变 this 指向
      场景一:div 上绑定onclick 事件,this 指向的是div。如果onclick中定义了函数func,func调用时,func 内部 this指向window;这时可以修改this指向。
    document.getElementById('div1').onclick = function(){
        console.log(this.id); // div1
        var func = function(){
            console.log(this.id) // undefined
        }
        func()
    }
    
    // 改变 this 指向
    document.getElementById('div1').onclick = function(){
        console.log(this.id); // div1
        var func = function(){
            console.log(this.id) // undefined
        }
        func.call(this)
    }
    

    2.Function.prototype.bind()
    首先将 this 保存,返回一个新函数fn ,fn执行时,实际上执行的是原函数。

    Function.prototype.bind = function(){
        var self = this, // 保存原函数
        context = [].shift.call( arguments ), // 需要绑定的this 上下文
        args = [].slice.call( arguments ); // 剩余的参数转成数组
        return function(){ // 返回一个新的函数
            return self.apply( context, [].concat.call( args, [].slice.call( arguments ) ) );
                // 执行新的函数的时候,会把之前传入的context 当作新函数体内的this
                // 并且组合两次分别传入的参数,作为新函数的参数
        }
    };
    
    var obj = {
        name: 'sven'
    };
    
    var func = function( a, b, c, d ){
        alert ( this.name ); // 输出:sven
        alert ( [ a, b, c, d ] ) // 输出:[ 1, 2, 3, 4 ]
    }.bind( obj, 1, 2 );
    
    func( 3, 4 );
    
    1. 借用其他对象的方法
      场景一:借用构造函数,用在构造函数继承
    var A = function( name ){
        this.name = name;
    };
    
    var B = function(){
        A.apply( this, arguments );
    };
    
    B.prototype.getName = function(){
        return this.name;
    };
    
    var b = new B( 'sven' );
    console.log( b.getName() ); // 输出: 'sven'
    

    场景二:arguments 类数组 使用数组的方法

    [].shift.call(arguments);
    [].push.call(arguments,1);
    Array.prototype.slice(arguments)
    

    相关文章

      网友评论

          本文标题:搞懂this、call、apply

          本文链接:https://www.haomeiwen.com/subject/svydyftx.html