4

原型与原型链

一.在了解原型之前需要提前了解的。
  • 1.工厂模式:创建一个函数,用特定的接口创建对象的细节。

    //现在要创建两个包含 “name”和“age”的对象,
    let tom ={name:"Tom",age:16}
    let lily = { name : "Lily",age:18}
    ……//所示数量足够具大 ,对象足够大 ,会花费大量的时间。下面的工厂模式能 很好的解决这一问题
       //工厂的就是只流水线不是所有都是要人工,节约人力成本。
      function person(name,age){
          let  obj = {}; //创建一个对象 ;
          obj.name = name ; //为对象添加细节
          obj.age = age ;
          obg.sayHello= function (){
            alert ("Hello"+this.name)
          };
          return obj;} //返回这个对象 
      let tom = person("Tom",16);
      let lily = person("Lily",18)     
    • 工厂函数的优势:避免的大量的重复代码;
    • 工厂函数的劣势:其创建的对象不能标志为一种特定的类型,没有解决对象识别的问题,不知道对象的类型。构造函数能很好的解决这个问题。
  • 2.构造函数:自定义构造函数,可以自定义对象类型的属性和方法。

      function Person(name,age){   
          this.name = name ; //为对象添加细节
          this.age = age ;
          this.sayHello= function (){
            alert ("Hello"+this.name)
          };
      let tom = new Person("Tom",16);
      let lily =new Person("Lily",18)    
    • new的过程做了三件事创建了一个对象;this指向这个对象;返回这个对象;
    • 两个实例对象都有一个属性constructor(构造函数),指向Person;这就是可以通过constructor判断对象类型的原理。
    • 存在的问题:构造函数的每个方法都要在实例上重新创建一遍,虽然函数名是一样的,但是不相等的。

二.原型模式
  • 1.如下,与构造函数模式不同的是,原型模式的每个实例都是访问同一属性同一函数,,函数不用重新创建。

    function Person () {
       this.x = 100;
    }
    Person.prototype.name = 'Tom';
    Person.prototype.age = 18;
    Person.prototype.sayHello =  function (){
      alert(`Hello${this.name}`)
    }
    let Tom = new Person()
    Tom.sayHello()
  • 2.原型对象

    • ①每个函数数据类型(普通函数,类)上,都有一个属性,叫prototype,是一个对象,这个函数指向函数的原型对象;
    • prototype这个对象上,自带一个属性,constructor指向当前这个类;
    • ③当为实例添加属性时,这个属性会屏蔽原型对象中保存的同名属性,但是事实阻止访问,并没有修改那个属性,若将同名的实例属性设置为null,同样会屏蔽,但是若用delete,则可以删除实例属性,可以重新访问原型中的属性。

      alert(Tom.age);  //18
      let Tom.age = 20;
      alert(Tom.age); //20
      delete Tom.age;
      alert(Tom.age); //18 

      clipboard.png

    • ④属性分为两种,来自实例(或者是构造函数)或是原型,原型上的属性和方法都是共有的,构造函数中的属性方法都是私有的,构造函数中的this都是实例。为什么私有属性(来自实例的属性)的查找等级要高呢?这就涉及到原型链。
  • 3.原型链:每个对象数据类型(普通对象,prototype,实例)都有一个属性,叫做__proto__

    • 比如我们console.log(Tom.age)的查找过程是怎样的呢?①首先在私有空间内查找age属性,私有空间的属性包括自身的属性和从类那里继承的属性,
    • ②找不到的话:通过__proto__去当前实例所属类的原型上进行查找,找到的话,说明是共有属性;
    • ③还找不到的话:继续通过__proto__去当前实例所属类的原型进行查找,找不到将继续通过__proto__一直找到Object
      clipboard.png
    • ④曲线代表Person的原型对象;曲线中,实例Tom通过__proto__指向Tom所属类(Person)的原型(Person Prototype);曲线③中,Person的原型对象通过__proto__找到Person对象的所属类,也就是Object(函数也是对象类,万物皆对象),指向它的原型(ObjectPrototype) ;曲线5中,指向Obeject所属类的原型,它的类就是它自己,所以此时不在有__proto__整个属性查找结束,这就是原型链的查找过程。
      clipboard.png
    • ⑤通过hasOwnProperty来判断属性,这个方法就是通过④中的查找过程,在基类Object中找到的。 判断属性是否在对象上“name” in Tom,其中in的查找过程也是通过上述的查找过程。

      function hasPrototypeProperty(object,attr){
         return !object.hasOwnProperty(attr) && (attr in object);}
  • 4.关于原型需要注意的几点

    • ①使用简单原型语法的时候,注意constructor的指向问题,

      Person.prototype={  //此时constructor指向Object构造函数
         name : 'Tom',
         age:18
      }
      Person.prototype={  //此时constructor指向Person
         constructor:Person,
         name : 'Tom',
         age:18
      }
    • ②添加和修改原型的属性和方法都能立即在所有对象实例中反应出来(即使先创建实例后修改原型);但是如果重写整个原型对象,创建在前的实例并不能获得重写后的属性或方法。这是因为重写原型之后是新生成原型对象,和声明在前的任何已经存在的实例都没有关系。

何凯
966 声望174 粉丝

Never too late to learn!