原型与继承(ES5)

38 阅读3分钟

一.原型

1.1对象的原型

  • 定义

    js当中每个对象都有一个特殊的内置属性[[ prototype ]] (隐式原型),这特殊的对象可以指向另一个对象

  • 作用

    1. 当我们通过引用对象的属性key来获取一个value时,它就会触发[[Get]]的操作
    2. 这个操作首先会检查该对象是否有对应的属性,若有就返回该属性
    3. 如果对象中没有该属性,那么就会访问对象[[prototype]]内置属性指向的对象上的属性
  • 获取方法

    方式一: 通过对象的__proto__属性可以获取到(浏览器提供,纯在一定的兼容性问题)
    方式二:通过Object.getPrototypeOf()方法获取
    

1.2函数对象的原型(重点显示原型)

  • 定义

(函数是特殊的对象)将函数看成一个普通对象是,它具备_ proto_(隐式原型)

作用:查找key对应的value时,会找到原型身上

将函数看作是一个函数时(非箭头函数),它是具备prototype(显式原型)

作用:用来构建对象时,给对象设置隐式原型的

  • 作用

    当一个函数被new操作符操作了会经历如下的过程。

    1. 在内存中创建一个新的对象(空对象)
    2. 这个对象内部的[[prototype]] (隐式原型)属性会被赋值为该构造函数的prototype属性
    3. 构造函数内部的this,会指向创建出来的新对象
    4. 执行函数的内部代码(函数体代码)
    5. 如果构造函数没有返回非空对象,则返回创建出来的新对象
  • 用处

    当多个对象拥有共同的值时,可以将该值放到构造函数对象的显式原型,由构造函数创建出来的所有对象,都会共享这些属性

    function Foo(name,age,height){
        this.name = name
        this.age = age
        this.height = height
        // 方法一:直接定义。(创建多个实例时,会创建多个函数对象)
        // this.put = function(){
        //   console.log(this.name,this.age,this.height);
        // }
       }
        
      //  方法二:将函数挂载到Foo的原型上
       Foo.prototype.put = function(){
          console.log(this.name,this.age,this.height);
        }
    ​
        var foo1 = new Foo('lsp',18,178)
    ​
        foo1.put()
    

1.2.1 constructor属性

原型对象上有一个属性叫做constructor

在显示原型上constructor指向当前的函数对象

   function Foo(){}
   var foo1 = new Foo()
   console.log(Foo.prototype.constructor);//function Foo
   console.log(foo1.__proto__.constructor);//function Foo
   console.log(foo1.__proto__.constructor.name);//Foo

二. 继承(ES5)

2.1前言

面向对象的三大特性:封装,继承,多态

  • 封装:将属性和方法封装到一个类中
  • 继承:减少重复代码的数量,也是多态的前提(纯面向对象中)
  • 多态:执行时表现出不同的形态

原型链:从一个对象上获取属性,如果当前对象中没有获取到就会去它的原型上获取

2.2利用原型链实现继承

2.2.1方法继承

  // 父
   function Person(name,age,height){
    this.name = name
    this.age = age
    this.height = height
   }
   Person.prototype.eating=function(){
    console.log("eating");
   }
  //  子
   function Student(){}
​
  //  方式一:父类原型直接赋值给子类原型
  // 缺点:父子类共享一个原型对象,修改其中一个,另一个也会跟着变化
  //  Student.prototype=Person.prototype
  //  var stu=new Student()
  //  stu.eating()
​
  // 方法二:创建一个父类的实例对象,用这个实例对象作为子类的原型对象
  //缺点:只能继承方法,属性时无法继承的
  var p = new Person('lsp','18','188')
  Student.prototype = p
  var stu = new Student()
  stu.eating()

2.2.2属性继承

 // 父
   function Person(name,age){
    this.name = name
    this.age = age
   }
   Person.prototype.eating=function(){
    console.log("eating");
   }
  //  子
   function Student(name,age,height,hobby){
    // this.name = name
    // this.age = age
     
    // 重点:借用构造函数
    Person.call(this,name,age)
     
    this.height = height
    this.hobby = hobby
   }

2.2.3组合借用继承

将方法继承第二种与属性继承结合起来被称作组合借用继承,有缺陷

2.2.4寄生组合式(最终)

  //寄生组合式函数
  //将该函数封装起来,方便使用
  function createObject(o){
    function F(){}
    F.prototype = o
    return new F()
  } 
  function inherit(Subtype,SuperType){
    Subtype.prototype = createObject(SuperType.prototype)
    Object.defineProperty(Subtype.prototype,"constructor",{
      enumerable: false,
      configurable: true,
      writable: true,
      value: Subtype
    })
  }
  // -----------------
   function Person(name,age){
    this.name = name
    this.age = age
   }
   Person.prototype.eating=function(){
    console.log("eating");
   }
​
   function Student(name,age,height,hobby){
    Person.call(this,name,age)
    this.height = height
    this.hobby = hobby
   }
​
   inherit(Student,Person)
   Student.prototype.stydying = function(){
    console.log("studying");
   }
​
  //创建实例对象
  var stu = new Student('lsp','18','188',"肉蛋冲击")
  

2.2Object类是所有类的父类

所有对象的原型链终点是object(object的原型是null)