会js与jq的后端开发如何愉快的转前端:第二篇-第二节:es6-面向对象-把oop玩出花-原型prototype

构造函数的原型

  • 构造函数弊端

  • 构造函数的原型prototype

  • 原型和原型的作用

构造函数的弊端

浪费内存

构造函数的弊端:存在浪费内存的问题。

原型:每一个构造函数都有一个 prototype 属性,指向另一个对象。这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

原型的作用:资源共享。

为什么说 浪费内存?

写一个构造函数 用来创建学生对象

属性:姓名 年龄 身高 体重

行为:学习 吃饭 看片 把妹

function Student (name){

    this . name = name ;

    this . study = function (){

        console . log ( "好好学习 天天向上" ) ;

    }

}

如果构造函数没有参数,那么在调用的时候小括号可以省略

    var stu = new Student ( "胡一天" ) ;

    stu. study () ;

    var stu1 = new Student ( "沈悦" ) ;

    stu1. study () ;

上面两个对象的实际情况如下图:

会js与jq的后端开发如何愉快的转前端:第二篇-第二节:es6-面向对象-把oop玩出花-原型prototype

每个学生对象的study函数都是同一句代码,那同样的代码在内存里面占两份就不合适。
那么传统的解决方法是让所有的对象共用一个方法,在构造函数外部定义好该函数,将该函数赋值给构造函数内的方法。

使用这种方式存在的问题:
1就是全局变量增多造成污染,
2就是代码结构混乱,不易维护

构造函数的原型prototype (解决弊端)

原型定义

原型:每一个构造函数都有一个 prototype 属性,指向另一个对象。这个 prototype 就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

作用:资源共享

一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上

代码:

<body>

    <script>

        // 1. 构造函数的问题. 

        function Star(uname, age) {

            this.uname = uname;

            this.age = age;

            // this.sing = function() {

            //     console.log('我会唱歌');

            // }

        }

        Star.prototype.sing = function() {

            console.log('我会唱歌');

        }

        var dmm = new Star('大幂幂', 18);

        var bo = new Star('波波', 19);

        console.log(dmm.sing === bo.sing); // 如果不是公共方法,这里就是false

        // console.dir(Star);

        dmm.sing();

        bo.sing();

        // 2. 一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上

    </script>

</body>

对象原型的方法

proto 我自己的对象原型

对象身上系统自己添加一个__proto__ 指向我们构造函数的原型对象 prototype

<body>

    <script>

        function Star(uname, age) {

            this.uname = uname;

            this.age = age;

        }

        Star.prototype.sing = function() {

            console.log('我会唱歌');

        }

        var dmm = new Star('大幂幂', 18);

        var bo = new Star('波波', 19);

        dmm.sing();

        console.log(dmm); // 对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象 prototype

        console.log(dmm.__proto__ === Star.prototype);

        // 方法的查找规则: 首先先看dmm 对象身上是否有 sing 方法,如果有就执行这个对象上的sing

        // 如果没有sing 这个方法,因为有__proto__ 的存在,就去构造函数原型对象prototype身上去查找sing这个方法

    </script>

</body>

属性 constructor 指回 原来的构造函数

很多情况下,我们需要手动的利用 constructor 这个属性指回 原来的构造函数

为什么要指回去?

运用场景 :编写两个对象原型方法.
思考: 我能不能简单点优化代码?

Star.prototype.sing = function() {
    console.log('我会唱歌');
};
Star.prototype.movie = function() {
    console.log('我会演电影');
}

开始优化,合并 prototype,修改原型

<body>
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        // 2. 直接定义一个新的prototype
        // constructor 这个属性指回 原来的构造函数
        Star.prototype = {
            // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
            // 如果不指回去,此行注释,则无法找到原来的构造函数 
            // 打印Star.prototype.constructor dmm.__proto__.constructor 只有此构造函数中的 sing 与 movie
            // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
            constructor: Star, 
            sing: function() {
                console.log('一生一世');
            },
            movie: function() {
                console.log('php啊');
            }
        }
        var dmm = new Star('大幂幂', 18);
        var bo = new Star('波波', 19);
        console.log(Star.prototype);
        console.log(dmm.__proto__);
        console.log(Star.prototype.constructor);
        console.log(dmm.__proto__.constructor);
    </script>
</body>

如果不指向 原型,则打印如图:(直接把指向注释掉)

会js与jq的后端开发如何愉快的转前端:第二篇-第二节:es6-面向对象-把oop玩出花-原型prototype

指向原型:

会js与jq的后端开发如何愉快的转前端:第二篇-第二节:es6-面向对象-把oop玩出花-原型prototype

原型链

  1. 只要是对象就有__proto__ 原型, 指向原型对象
  2. 我们Star原型对象里面的__proto__原型指向的是 Object.prototype
  3. 我们Object.prototype原型对象里面的__proto__原型 指向为 null
<body>

    <script>

        function Star(uname, age) {

            this.uname = uname;

            this.age = age;

        }

        Star.prototype.sing = function() {

            console.log('我会唱歌');

        }

        var dmm = 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>

</body>

原型对象中this指向

在构造函数中,里面this指向的是对象实例
原型对象函数里面的this 指向的是 实例对象

<body>
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        var that;
        Star.prototype.sing = function() {
            console.log('我会唱歌');
            that = this; // 指向的是 实例对象 dmm
        }
        var dmm = new Star('大幂幂', 18);
        // 1. 在构造函数中,里面this指向的是对象实例 dmm
        dmm.sing();
        console.log(that === dmm);

        // 2.原型对象函数里面的this 指向的是 实例对象 dmm
    </script>
</body>

结果:

会js与jq的后端开发如何愉快的转前端:第二篇-第二节:es6-面向对象-把oop玩出花-原型prototype

true

原型对象的应用 扩展内置对象方法

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。

注意: 数组和字符串内置对象不能给原型对象覆盖操作Array.prototype ={},只能是Array.prototype.xx = function 追加 的方式。

<body>
    <script>
        // 原型对象的应用 扩展内置对象方法

        Array.prototype.sum = function() {
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
                sum += this[i];
            }
            return sum;
        };
        // 这一段会直接报错,js本身存在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>
</body>

执行截图:

会js与jq的后端开发如何愉快的转前端:第二篇-第二节:es6-面向对象-把oop玩出花-原型prototype

如果使用覆盖形式进行运行(把上面代码 注释的地方打开,追加形式扩展的sum 注释):


<body>

    <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>

</body>

执行结果:

会js与jq的后端开发如何愉快的转前端:第二篇-第二节:es6-面向对象-把oop玩出花-原型prototype

数组和字符串内置对象不能给原型对象覆盖操作Array.prototype ={},只能是Array.prototype.xx = function 追加 的方式。

本作品采用《CC 协议》,转载必须注明作者和本文链接
嗨,我是波波。曾经创业,有收获也有损失。我积累了丰富教学与编程经验,期待和你互动和进步! 公众号:上海PHP自学中心 付费知识星球:破解面试:程序员的求职导师
讨论数量: 1

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
司机 @ 欣昊玉
文章
273
粉丝
339
喜欢
557
收藏
1103
排名:64
访问:12.1 万
私信
所有博文
社区赞助商