zoukankan      html  css  js  c++  java
  • JavaScript设计模式与开发实践 | this、call和apply

    原文地址:http://segmentfault.com/a/1190000003959359

    this

    JavaScript的this总是指向一个对象,至于指向哪个对象,是在运行时基于函数的执行环境的动态绑定的,而非函数被声明时的环境。

    this的指向

    this的指向大致可以分为以下4类:

    • 作为对象的方法调用

    • 作为普通函数调用

    • 构造器调用

    • Function.prototype.callFunction.prototype.apply调用

    1.作为对象的方法调用

    当函数作为对象的方法被调用时,this指向该对象:

    // 声明obj对象
    var obj = {
        a: 'a属性的值',    // a 属性
        getA: function(){  // getA()方法
            console.log(this === obj);  // 输出:true
            console.log(this.a);  // 输出: a属性的值
        }
    };
    
    obj.getA();

    2.作为普通函数调用

    当函数不作为对象的属性被调用时,也就是以普通函数方式,this指向全局对象。在浏览器的JavaScript里,全局对象是window对象。

    window.name = 'globalName';  // 声明全局对象的name属性
    
    var getName = function(){  // 定义getName()函数
        return this.name;
    };
    
    // 调用函数
    console.log(getName());  //输出: globalName
    
    window.name = 'globalName';  // 声明全局对象的name属性
    
    var myObject = {  // 声明myObject对象
        name: 'objectName';
        getName: function(){  // 定义getName()方法
            return this.name;
        }
    }
    
    var getName = myObject.getName;  // 将getName()方法赋给变量getName
    
    console.log(getName());  // 输出: globalName

    3.构造器调用

    JavaScript没有类,但可以从构造器中创建对象,也提供了new运算符用于调用构造器。

    大部分JavaScript函数都可以当作构造器使用。构造器的外表跟普通函数一样,他们的区别在于被调用的方式。即,使用new运算符创建对象时,就是将函数当作构造器调用。当用new运算符调用函数时,该函数总会返回一个对象,此时,构造器里的this指向返回的这个对象。

    var myClass = function(){
        this.name = 'className';
    };
    
    var obj = new myClass();
    console.log(obj.name);  // 输出:seven
    

    但,如果构造器显式地返回了一个object类型的对象,那么此函数将返回这个object类型的对象,而不是函数本身所定义的对象,例如:

    var myClass = function(){
        this.name = 'className';
        return {  //显式地返回一个对象
            name: 'anne'
        }
    };
    
    var obj = new myClass();
    console.log(obj.name);  //  输出:anne

    而,如果构造器不显式地返回任何数据,或返回一个非对象类型的数据,就不会造成上述情形。

    var myClass = function(){
        this.name = 'className';
        return 'anne';  // 返回string类型
    };
    
    var obj = new myClass();
    console.log(obj.name);  //  输出:className
    

    4.Function.prototype.call 或 Function.prototype.apply调用

    跟普通函数调用相比,用 Function.prototype.call 或 Function.prototype.apply 可以动态地改变传入函数的this。

    var A = {
        name: 'ObjectA',
        getName: function(){
            return this.name;
        }
    };
    
    var B = {
        name: 'ObjectB'
    };
    
    console.log(A.getName()); // 作为对象的方法调用,输出:ObjectA
    console.log(A.getName.call(B)); // 输出:ObjectB

    丢失的this

    我们经常会因为this的指向与我们的期待不同,而出现undefined的情况,例如:

    var obj = {
        name: 'objName';
        getName: function(){
            return this.name;
        }
    };
    
    // 作为对象的方法调用,指向obj对象
    console.log(obj.getName());   // 输出:objName
    
    // 作为普通函数调用,指向全局对象window,name属性尚未定义
    var getName2 = obj.getName;
    console.log(getName2());  // 输出:Lundefined

    call 和 apply

    ECAMScript3给Function的原型定义了两个方法,分别是Function.prototype.call 或 Function.prototype.apply。在一些函数式风格的代码编写中,call和apply方法尤为有用。

    call和apply的区别

    Function.prototype.call 或 Function.prototype.apply的作用一模一样,区别仅在于传入参数形式的不同。

    apply接受两个参数,第一个参数制定了函数体内this对象的指向,第二个函数为一个带下标的集合,这个集合可以是数组,也可以是类数组。apply方法把这个集合中的元素作为参数传递给被调用的函数。

    var func = function(a, b, c){
      console.log([a, b, c]);  // 输出:[1,2,3]
    };
    
    func.apply(null, [1, 2, 3]);

    call传入的参数数量不固定,第一个参数也是代表了函数体内的this指向,从第二个参数开始往后,每个参数依次被传入函数:

    var func = function(a, b, c){
      console.log([a, b, c]);  // 输出:[1,2,3]
    };
    
    func.call(null, 1, 2, 3);

    当调用一个函数时,JavaScript的解释器并不会计较形参和实参在数量、类型、以及顺序上的区别,JavaScript的参数在内部就是用一个数组来表示的。从这个意义上说,apply比call的使用率更高,我们不必关心具体有多少参数被传入函数,只要用apply一股脑地推过去就可以了。

    当使用call或apply的时候,如果我们传入的第一个参数为null,函数体内的this会指向默认的宿主对象,在浏览器中则是window:

    var func = function(a, b, c){
      console.log(this);
    };
    
    func.apply(null, [1, 2, 3]);  //输出:window对象
    func.call(null, 1, 2, 3);  //输出:window对象

    call和apply的用途

    • 改变this指向

    • Function.prototype.bind

    • 借用其他对象的方法

    1.改变this指向

    call和apply最常见的用途是改变函数内部的this指向:

    var A = {
      name: 'nameA';
    };
    
    var B = {
      name: 'nameB';
    };
    
    window.name = 'nameWindow';
    
    var getName = function(){
      conlole.log(this.name);
    };
    
    getName();  // 以普通函数调用,指向了window对象,输出:nameWindow
    getName.call(A);  // 改变了this的指向,指向了传入的对象,输出:nameA
    getName.call(B);  // 改变了this的指向,指向了传入的对象,输出:nameB

    2.Function.prototype.bind

    大部分高级浏览器都实现了内置的Function.prototype.bind,用来指定函数内部的this指向。
    若没有原生的Function.prototype.bind实现,可以通过模拟一个:

    Function.prototype.bind = function(context){
      var self = this;  // 保存原函数
      return function(){  // 返回一个新的函数
          return self.apply(context, arguments);  // 执行新函数的时候,会把之前传入的context当作新函数体内的this
      }
    };
    
    var obj = {
     name: "objName"
    };
    
    var func = function(){
      console.log(this.name);  // 输出:objName
    }.bind(obj);
    
    func();
  • 相关阅读:
    建表关系
    设计模式-策略模式
    设计模式-适配器模式
    在线操作word和在线预览查找的资料记录
    Elasticsearch 使用-安装
    12-factors
    Kafka 使用-安装
    Apache Zookeeper 使用-安装
    Java 使用-安装
    设计模式-模板方法模式
  • 原文地址:https://www.cnblogs.com/zoumiaomiao/p/4950931.html
Copyright © 2011-2022 走看看