• 原型和继承


    原型和继承

    一、原型

    说到原型之前,先复习一下对象创建方式:

    1. 使用对象字面量的形式创建 (最简单)

    <script>
    
      var obj = { a: 1}
      obj.b = 2
      console.log(obj)
    
    </script>
    

    2. 使用工厂函数的方式

    <script>
    
      function CreatePerson (name, age) {
        var obj = new Object ()
        obj.name = name
        obj.age = age
        obj.sayHi = function () {
          console.log('哈喽')
        }
        return obj
      }
      var person1 = CreatePerson('张三', 18)
      var person2 = CreatePerson('李四', 20)
    
    </script>
    

    3. 使用构造函数的方式(对工厂函数的改进)

    <script>
    
      function Person (name, age) {
        this.name = name
        this.age = age
        this.sayHi = function () {
          console.log('哈喽')
        }
      }
      var person1 = new Person('张三',18)
      var person2 = new Person('李四',20)
    
    </script>
    
    • 用new操作符调用函数时,会自动执行下面的操作
    • 创建(或构造)一个新的对象
    • 这个新对象会执行prototype连接
    • 这个新对象会绑定到函数调用的this
    • 如果函数没有返回其他对象, 那么new表达式中的函数调用自动返回这个新对象
    //我们来举个例子
    var Foo() {
      //这是一个普通函数
    }
    Foo() //普通函数调用
    var a = new Foo()//在普通调用前加一个new关键字后,这个调用就变成了构造函数调用
    
    • new会劫持所有普通函数,并用构造函数的形式调用它

    var a = new Foo() 这句话的执行机制: Foo是一个普通函数,在使用new调用时,它就会构造一个对象,并且赋值给a

    • 简单点来说 js中的 构造函数 :所有带new的函数调用
    • 用new的方法来调用Foo会产生一个新对象a,这个新对象的内部链接__proto__会关联到Foo的原型对象上
      a.proto == Foo.prototype

    4. 使用原型的方式

    • 原型的初步模式
    <script>
      function Person () { }
    
      Person.prototype.name = '张三'
      Person.prototype.age = 18
      Person.prototype.sayHi = function () {
          console.log('哈喽')
        }
    
      var person1 = new Person()
      var person2 = new Person()
    </script>
    
    
    • 优点:原型中所有的属性和方法都可以被实例对象共享
    • 缺点:所有的实例对象都公用一套原型和方法,没有差异化,不能修改
    我们来了解一下实例对象、构造函数和原型之间的联系

    我们来了解一下实例对象、构造函数和原型之间的联系

    • 优化模式(原型重构)
      上面例子,每次添加一个属性和方法,就要重新写一遍,比较繁琐,现在把这些属性和方法都放入一个对象中,再赋值给Person的原型
    <<script>
    
      function Person () { }
    
      Person.prototype = {
        name: '张三',
        age: 18,
        sayHi: unction () {
          console.log('哈喽')
        }
      }
    
      var person1 = new Person()
      var person2 = new Person()
    
      console.log(person1.constructor == Person) // false
      console.log(person1.constructor == Object) // true
    
    </script>
    
    
    • js 中所有的对象都有一个特殊的内置属性[[prototype]] ,这个属性其实就是用来对其他对象的引用
    • 这种写方法改变了原型内部的constructor指针的属性了 ,不再指向Person函数了,如果这个指针很重要,那么可以手动设置
    <<script>
    
      function Person () { }
    
      Person.prototype = {
        constructor:Person,
        name: '张三',
        age: 18,
        sayHi: unction () {
          console.log('哈喽')
        }
      }
    
      var person1 = new Person()
      var person2 = new Person()
    
      console.log(person1.constructor == Person) // true
      console.log(person1.constructor == Object) //false
    
    </script>
    
    
    如果要访问对象中的属性,我们要经过哪些机制呢?
    <script>
    
      var obj = {
        a: 2
      }
    
      console.log(obj.a) // 2
    
    </script>
    
    
    

    总结:prototype 的机制

    prototype机制就是存在于对象中的一个内部链接,它会引用其他对象。

    这个链接的作用就是: 如果在对象上没有找到需要的属性或者方法,引擎就会在prototype关联的对象上进行查找。如果在后者的对象中没有找到这个属性和方法,引擎就会继续查找他的prototype,以此内推,如果没有找到,就会返回underfind。
    这就是所谓的原型链。(有点像嵌套的作用域链)


    二、继承

    构造函数就是初始化一个实例对象,对象的prototype属性是继承一个实例对象。

    继承的方式:

    1. 通过原型的方式继承

    <script>
      function Bird(legNum, wingNum) {
        this.leg = legNum;
        this.wing = wingNum;
      }
      Bird.prototype.appearance = function () {
        console.log('鸟类都有漂亮的羽毛')
      }
      function Swan (beh,legNum,wingNum) {
        this.behaviour =  beh;
      }
      Swan.prototype = new Bird(2,2,);// 继承了Bird中的属性和方法,但是不能通过传参修改这些值
    
      Swan.prototype.story = function () {
        console.log('是由丑小鸭变成的');
      }
       // 白天鹅
      var whiteSwan = new Swan('会游泳')
      console.log(whiteSwan)
      whiteSwan.appearance() // 鸟类都有漂亮的羽毛
      whiteSwan.story() // 是由丑小鸭变成的
    </script>
    
    • 优点:所有的属性和方法均可以进行继承
    • 缺点:如果继承了属性,属性值在后期无法修改

    2. 使用call()方法调用实现继承

    <script>
      function Person (name, age) {
      this.name = name;
      this.age = age;
      }
      Person.prototype.behaviour = function () {
        console.log('大家好,我是一类人')
      }
    
    
      function Man(name, age, gender) {
        Person.call(this, name ,age);
        this.gender = gender;
      }
      //可以创建多个实例对象
      var man1 = new Man('张三', 18, '男')
      var man2 = new Man('李四', 20, '女')
      //但是 不能调用原型中的属性和方法
      console.log(man1)
      console.log(man2)
      man1.behaviour()//会报错,这种方法继承不到Person函数中原型中的属性方法
    </script>
    
    

    call(this, 参数1 , 参数2...)

    • 使用call方法调用中的参数this 把this指向了调用者
    • 优点:可以继承所有在构造函数体中书写的属性,并且可以进行传参
    • 缺点: 无法继承原型中的属性和方法

    3. 组合方式继承(使用call和原型的方式组合继承)

    • call方法:继承构造函数体中的属性,并可以传参修改
    • 原型方式:可以继承原型链中的属性
    • 看实例
    <script>
     // 组合继承
     function Person (name, age) {
        this.name = name;
        this.age = age;
      }
      //Person的原型
      Person.prototype.behaviour = function () {
        console.log('大家好,我是一类人')
      }
    
      //console.log(Person())//underfined
    
      //Man 的构造函数
      function Man(name, age, gender) {
        //call调用的方法进行继承,这个方法改变了Man这个构造函数内部的指向(即constructor指针)
        Person.call(this, name ,age);//第二次调用Person
        this.gender = gender;
      }
      //这里的通过new创建了一个实例对象,并把这个实例对象赋值给了Man的原型对象, 所以改变了 Man原型对象的内部指针(constrouctor)
      //原本Man 的原型对象中的constructor指向Man这个函数的,但是现在被改变成指向Person这个函数了
    
      Man.prototype =  new Person() //第一次调用Person
      //Man 原型对象中的方法
      Man.prototype.action = function () {
        console.log('吃饭、睡觉、打豆豆')
      }
      //可以创建多个实例对象
      var man1 = new Man('张三', 18, '男')
      var man2 = new Man('李四', 20, '女')
      console.log(man1)
      man1.behaviour()
      man1.action()
    </script>
    
    
    
    • 优点:所有的属性和方法均可以被继承,并且所有的属性均可自由传参。
    • 缺点:每调用一次Man构造函数时,就会调用两次Person函数

    4. 拷贝继承方法 (将原对象进行遍历,把它的属性和方法赋值给新的对象)

    • 看代码
    <script>
    //构造函数
      function Person (name, age) {
        this.name = name;
        this.age = age;
      }
      Person.prototype.behaviour = function () {
        console.log('大家好,我是一类人')
      }
    
      function Man (name, age, gender) {
        this.gender = gender
        var person = new Person(name,age)
        // 在Man函数内部对Person进行遍历, 把People具有的属性赋值给Man
        for (var k in person) {
          this[k] = person[k]
        }
      }
      Man.prototype.action = function () {
        console.log('吃饭、睡觉、打豆豆')
      }
      //创建实例对象man
      var man = new Man('张三', 18, '男')
      var man2 = new Man('李四', 20, '女')
      console.log(man.name)
      console.log(man.age)
      console.log(man)
      console.log(man2)
      man.behaviour()
    
    </script>
    
    • 优点:可以传参,可以继承到原型中属性和方法

      5. this的方式(这种方式与call的方式类似)

    <<script>
       //构造函数
      function Person (name, age) {
        this.name = name
        this.age = age
      }
      Person.prototype.behaviour = function () {
        console.log('大家好,我是一类人')
      }
    
      function Man (name, age, gender) {
      //在Man中添加一个属性people,把People这个函数体赋值给Man的属性people,然后再进行调用
        this.people = Person
        //进行调用
        this.people(name, age)
    
        this.gender = gender
      }
      Man.prototype = function () {
        console.log('吃饭、睡觉、打豆豆')
      }
      //实例对象
      var man = new Man('张三', 18 , '男')
    
      console.log(man)
      man.behaviour()// 报错  不能继承到Person原型中的属性方法
      </script>
    
    
    • 优点: 可以传参
    • 缺点:原型内的方法无法继承,并且还多了一个属性people
  • 相关阅读:
    等式
    Lemon 评测软件用法
    同花顺
    浅谈二分图的最大匹配和二分图的KM算法
    LCT总结
    5.30模拟赛
    树上斜率优化
    5.22 noip模拟赛
    KMP,HASH,Trie,AC自动机
    splay总结
  • 原文地址:https://www.cnblogs.com/queen-live/p/7846415.html
Copyright © 2020-2023  润新知