• 面向对象(一)-读书笔记


    一、工厂模式

    鉴于ECMAScript无法创建类,所以开发人员发明了一种函数,用函数来封装以特定接口创建对象的细节。举栗:

    function createPerson(name,age,job){
      var o = new Object();
      o.name = name;
      o.age = age;
      o.job = job;
      o.sayName = function(){
          console.log(this.name);  
      } 
       return o;       
    }  
    
    var person1 = createPerson("Andy",28,"Actor");
    var person2 = createPerson("Lily",22,"Nurse");

    函数createPerson()能够根据接收的参数来构建一个包含所有必要信息的Person对象。可以无数次调用这个函数,每次它都会返回一个包含三个属性一个方法的对象。

    工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

    二、构造函数

    举栗:

    function Person(name,age,job){
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = function(){
        console.log(this.name);
      }        
    }
    
    var person1 = new Person("Andy",28,"Actor");
    var person2 = new Person("Lily",22,"Nurse");

    Person()和createPerson()之间的不同:

      a.没有显式地创建对象;

      b.直接将属性和方法赋给了this;

      c.没有return语句;

      d.函数名第一个字母大写,主要为了区别于其他函数。

    要创建Person的新实例,必须使用new操作符。用这种方式调用构造函数会经历以下4个步骤:

      a.创建一个新对象;

      b.将构造函数的作用域赋给新对象(因此this就指向了这个新对象);

      c.执行构造函数中的代码(为这个新对象添加属性);

      d.返回新对象。

    例子的最后,person1和person2分别保存这个Person的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person。验证如下:

    console.log(person1.constructor == Person);   //true
    console.log(person2.constructor == Person);   //true

     对象的constructor属性最初是用来标识对象类型的,但是用instanceof检测对象类型更好一些。上面的例子中创建的所有对象既是Object的实例,也是Person的实例。验证如下:

    console.log(person1 instanceof Object);     //true
    console.log(person1 instanceof Person);     //true
    
    console.log(person2 instanceof Object);     //true
    console.log(person2 instanceof Person);     //true

    构造函数模式胜过工厂模式的地方在于:创建自定义的构造函数可以将它的实例标识为一种特定的类型。

     1.将构造函数当作函数

    构造函数与其他函数的唯一区别,在于调用它们的方式不同。构造函数也是函数,所以不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那它都可以作为构造函数;而任何函数,如果不通过new操作符来调用,那它跟普通函数没什么差别。举栗:

    //当作构造函数使用
    var person = new Person("Andy",28,"Actor");
    person.sayName();        //"Andy"
    
    //作为普通函数调用
    Person("Lily",22,"Doctor")   //添加到window
    window.sayName();       //"Lily"
    
    //在另一个对象的作用域中调用
    var o = new Object();
    Person.call(o,"Joe",25,"Nurse");
    o.sayName();          //"Joe"

    以上前两行代码是构造函数的典型用法,就是用new操作符创建一个新的对象。

    中间两行代码不用new操作符调用Person对象,属性和方法都被添加给了全局对象window。

    最后两行代码使用call()(或者apply())在某个特殊对象的作用域中调用Person()函数,那么对象o就拥有了所有的属性和方法。

    2.构造函数的问题

    构造函数主要问题:每个方法都要在每个实例上重新创建一遍。

    前面的栗子中,person1和person2都有一个名为sayName()的方法,但那两个方法不是同一个Function的实例。

    ECMAScript中的函数是对象,因此每定义一个函数,就是实例化了一个对象。

    从以上逻辑角度,此时的构造函数也可以这样定义。

    function Person(name,age,job){
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = new Function("console.log(this.name)"); //与声明函数在逻辑上是等价的        
    }

     从这个角度看构造函数,更容易明白每个Person实例都包含一个不同的Function实例的本质。以这种方式创建函数,会导致不同的作用域链和标识符解析,但创建Function新实例的机制仍然是相同的。因此,不同实例上的同名函数是不相等的,证明如下:

    console.log(person1.sayName == person2.sayName);    //false

    创建两个完成同样任务的Function实例没有必要,我们可以通过this对象,把函数绑定到特定对象上面。举栗:

    function Person(name,age,job){
      this.name = name;
      this.age = age;
      this.job = job;
      this.sayName = sayName;
    }
    
    function sayName(){
      console.log(this.name);  
    }
    
    var person1 = new Person("Andy",28,"Actor");
    var person2 = new Person("Lily",22,"Nurse");

    上面的栗子,我们把方法sayName()函数的定义转移到了构造函数外部。而在构造函数内部,我们将sayName属性设置成全局的sayName函数。因为sayName包含的是一个指向函数的指针,所以person1和person2对象就共享了在全局作用域中定义的同一个sayName()函数。

    但是问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,所以这让全局作用域有点儿名不副实。再者,如果对象需要定义很多方法,那么就要定义很多个全局函数,所以我们这个就谈不上封装性了。

    参考资料

    《javascript高级程序设计(第3版)》第6章 面向对象的程序设计

  • 相关阅读:
    Web服务技术协议:REST与SOAP
    几种常见的Web服务器
    在浏览器中输入网址后是怎么跳转到指定的服务器的
    forward(请求转发)和redirect(重定向)的区别
    Hook钩子编程
    闭包
    JSP
    临界区与锁
    进程
    LeetCode Search for a Range
  • 原文地址:https://www.cnblogs.com/winteronlyme/p/6704748.html
Copyright © 2020-2023  润新知