• JS构造函数和原型


    一 构造函数创建对象的缺陷

    在ES6之前,对象不是基于类创建的,而是用一种称为构造函数的特殊函数来定义对象和它们的特征。

    <script>
            //  利用构造函数创建对象
            function Star(uname, age) {
                this.uname = uname;
                this.age = age;
                this.sing = function() {
                    console.log('我会唱歌');
                }
            }
    
            var ldh = new Star('刘德华', 18);
            var zxy = new Star('张学友', 19);
            console.log(ldh);
            ldh.sing();
            zxy.sing();
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 我们虽然可以通过构造函数创建对象,但是构造函数创建对象的缺陷在于,它会重复创建方法,浪费内存;
    • 不同的实例对象共用同一个方法,但是在执行构造函数代码创建对象时,它会给每个实例对象创建一个方法。

    在这里插入图片描述
    我们希望所有的实例对象使用同一个函数,这样比较节省内存,那么应该怎么做呢?这就需要用到接下来介绍的原型。

    二 原型 prototype

    • 构造函数通过原型分配的函数是所有对象所共享的
    • JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。注意这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
    • 我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
    1. 原型是什么 ?
      一个对象,我们也称为 prototype 为原型对象。
    2. 原型的作用是什么 ?
      共享方法。
    <script>
        // 1. 构造函数的问题. 
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
            // this.sing = function() {
            //     console.log('我会唱歌');
            // }
        }
        Star.prototype.sing = function() {
            console.log('我会唱歌');
        }
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 19);
        console.log(ldh.sing === zxy.sing);
        // console.dir(Star);
        ldh.sing();
        zxy.sing();
        // 2. 一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    构造函数、实例对象和原型对象三者之间的关系

    构造函数会有一个prototype属性指向构造函数的原型对象
    构造函数创建出来的实例对象会有一个__proto__属性指向构造函数的原型对象
    构造函数的原型对象会有一个constructor属性指回构造函数
    因此,构造函数、实例对象和原型对象三者之间有如下关系:
    在这里插入图片描述

    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function() {
            console.log('我会唱歌');
        }
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 19);
        ldh.sing();
        console.log(ldh); // 对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象 prototype
        console.log(ldh.__proto__ === Star.prototype); // true
        console.log(ldh.__proto__.constructor === Star.prototype.constructor); // true
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    三 原型链

    3.1 原型链终点

    • 每个对象身上都有__proto__属性指向其原型
    • 由于js中对象的基类是Object,所以沿着__proto__属性往上查找,最终会查找到Object.prototype(即Object的原型对象)
    • Object.prototype.__proto__就等于null
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function() {
            console.log('我会唱歌');
        }
        var ldh = new Star('刘德华', 18);
        // 1. 只要是对象就有__proto__ 原型, 指向原型对象
        console.log(Star.prototype);
        console.log(Star.prototype.__proto__ === Object.prototype);
        // 2.我们Star原型对象里面的__proto__原型指向的是 Object.prototype
        console.log(Object.prototype.__proto__);
        // 3. 我们Object.prototype原型对象里面的__proto__原型  指向为 null
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    3.2 JS成员查找机制

    • 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
    • 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
    • 如果还没有就查找原型对象的原型(Object的原型对象)。
    • 依此类推一直找到 Object 为止(null)。
    • __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
    • 一句话概括,就是沿着原型链往上查找。
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function() {
            console.log('我会唱歌');
        }
        Star.prototype.sex = '女';
        // Object.prototype.sex = '男';
        var ldh = new Star('刘德华', 18);
        ldh.sex = '男';
        console.log(ldh.sex);
        console.log(Object.prototype);
        console.log(ldh);
        console.log(Star.prototype);
        console.log(ldh.toString());
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    四 原型的应用

    4.1 扩展内置对象

    可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。
    注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {} ,只能是 Array.prototype.xxx = function(){} 的方式。

    <script>
        // 原型对象的应用 扩展内置对象方法
        Array.prototype.sum = function() {
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
                sum += this[i];
            }
            return sum;
        };
        // Array.prototype = {
        //     sum: function() {
        //         var sum = 0;
        //         for (var i = 0; i < this.length; i++) {
        //             sum += this[i];
        //         }
        //         return sum;
        //     }
        // }
        var arr = [1, 2, 3];
        console.log(arr.sum());
        console.log(Array.prototype);
        var arr1 = new Array(11, 22, 33);
        console.log(arr1.sum());
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    4.2 实现继承

    ES6之前并没有给我们提供 extends 继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。

    • 通过借用父构造函数继承父类属性(借助call函数,改变父构造函数的this指向
    • 通过原型对象继承父类方法( 让子类的 prototype 原型对象 = new 父类()
    <script>
          // 借用父构造函数继承属性
          // 1. 父构造函数
          function Father(uname, age) {
              // this 指向父构造函数的对象实例
              this.uname = uname;
              this.age = age;
          }
          Father.prototype.money = function() {
              console.log(100000);
          };
          // 2 .子构造函数 
          function Son(uname, age, score) {
              // this 指向子构造函数的对象实例
              Father.call(this, uname, age);
              this.score = score;
          }
          // Son.prototype = Father.prototype;  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
          Son.prototype = new Father();
          // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
          Son.prototype.constructor = Son;
          // 这个是子构造函数专门的方法
          Son.prototype.exam = function() {
              console.log('孩子要考试');
          }
          var son = new Son('刘德华', 18, 100);
          console.log(son);
          console.log(Father.prototype);
          console.log(Son.prototype.constructor);
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    五 类的本质

    1. class本质还是function
    2. 类的所有方法都定义在类的prototype属性上
    3. 类创建的实例,里面也有__proto__ 指向类的prototype原型对象
    4. 所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
    5. 所以ES6的类其实就是语法糖.
    6. 语法糖:语法糖就是一种便捷写法. 简单理解, 有两种方法可以实现同样的功能, 但是一种写法更加清晰、方便,那么这个方法就是语法糖
    <script>
        // ES6 之前通过 构造函数+ 原型实现面向对象 编程
        // (1) 构造函数有原型对象prototype 
        // (2) 构造函数原型对象prototype 里面有constructor 指向构造函数本身
        // (3) 构造函数可以通过原型对象添加方法
        // (4) 构造函数创建的实例对象有__proto__ 原型指向 构造函数的原型对象
        // ES6 通过 类 实现面向对象编程 
        class Star {
        }
        console.log(typeof Star);
        // 1. 类的本质其实还是一个函数 我们也可以简单的认为 类就是 构造函数的另外一种写法
        // (1) 类有原型对象prototype 
        console.log(Star.prototype);
        // (2) 类原型对象prototype 里面有constructor 指向类本身
        console.log(Star.prototype.constructor);
        // (3)类可以通过原型对象添加方法
        Star.prototype.sing = function() {
            console.log('冰雨');
        }
        var ldh = new Star();
        console.dir(ldh);
        // (4) 类创建的实例对象有__proto__ 原型指向 类的原型对象
        console.log(ldh.__proto__ === Star.prototype);
        i = i + 1;
        i++
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    修炼离线:(二)sqoop插入hbase 脚本(增量)
    百度飞浆EISeg高效交互式标注分割软件的使用教程
    报表查询栏位的实现——输入框,下拉列表,日期时间选择
    SQL零基础入门教程,贼拉详细!贼拉简单! 速通数据库期末考!(七)
    Spring-Cloud-Zipkin-05
    [HCTF 2018]WarmUp1
    MySQL索引下推
    springboot+vue+elementui买菜优选系统java
    题目 1053: 二级C语言-平均值计算(python详解)——练气三层初期
    设计模式(十四)----结构型模式之组合模式
  • 原文地址:https://blog.csdn.net/weixin_47750287/article/details/126477553