资讯专栏INFORMATION COLUMN

深入浅出面向对象和原型【番外篇——重新认识new】

Apollo / 1610人阅读

摘要:前言我们在深入浅出面向对象和原型概念篇在这篇文章中了解到了如何使用解决重复创建浪费内存的问题,其中的关键就是,那么这篇文章让我们来重新了解的前世今生一个苦逼年级主任的故事开学啦高一年级主任龚主任需要为全年级每一位理科班新生录入学号并为每一位

前言

我们在深入浅出面向对象和原型【概念篇2】在这篇文章中了解到了如何使用new Function解决重复创建浪费内存的问题,其中的关键就是new,那么这篇文章让我们来重新了解new的前世今生

一个苦逼年级主任的故事

开学啦~~~高一年级主任龚主任需要为全年级每一位理科班新生录入学号并为每一位学生生成相关档案
不仅要自己留一份而且要把每一个档案都上传到学校资料库
哇,全年级一千个学生,一个个输入,不要命啦?
还好龚主任学过编程

    // 先造一个对象,把相关数据都先写进去,看看是啥样的
    var 学生 = {
        学号: 1,
        年级: "高一",
        所选方向: "理科班",
        上传资料: function () {/*上传资料的代码*/
        }
    }
    // 不错,档案大致就是如此
    
    // 再来个数组自己留着作为备份
    // 那么循环一千次吧
    var 全年级学生 = []
    for (var i = 0; i < 1000; i++) {
        var 学生 = {
            学号: i,
            年级: "高一",
            所选方向: "理科班",
            上传资料: function () {/*上传资料的代码*/}
        }
        全年级学生.push(学生)
    }

龚主任突然想到,他昨天晚上在才在segmentfault上看到有关于内存浪费的文章——深入浅出面向对象和原型【概念篇2】
那么他写的这个代码就是典型的内存浪费啊
每个学生除了学号不同,其它都相同,咋办呢?
哎对了,那篇文章说可以通过原型和原型链解决这个问题
那么试试吧

    // 先创建一个学生原型,然后把相同的代码都放在这里
    var 学生原型 = {
        年级: "高一",
        所选方向: "理科班",
        上传资料: function () {/*上传资料的代码*/}
    }
    // 重新写循环代码
    var 全年级学生 = []
    for (var i = 0; i < 1000; i++) {
        var 学生 = {
            学号: i
        }
        // 还记得吗,每个对象自动带有__proto__属性
        // 不过在这里__proto__属性的指向需要我们自己去设定
        学生.__proto__ = 学生原型

        全年级学生.push(学生)
    }

好了,大功告成,这下内存不浪费了
但是,龚主任听说程序猿写代码都追求可读性强,他这写的太不优雅了
再改改吧

    // 优雅的代码离不开封装,现在让我们来封装封装吧
    function 学生(学号) {
        // 我们先建立一个临时对象,把例如学号之类需要改变的属性放进去
        var 临时对象 = {}
        临时对象.学号 = 学号

        // 再把临时对象的__proto__手工绑定到学生.原型
        临时对象.__proto__ = 学生.原型

        return 临时对象
    }

    学生.原型 = {
        年级: "高一",
        所选方向: "理科班",
        上传资料: function () {/*上传资料的代码*/
        }
    }

    // 好了,开始循环吧
    var 学生们 = []
    for (var i = 0; i < 1000; i++) {
        学生们.push(学生(i))
    }
好了,让我们先远离一下龚先生和他的代码,来看看到底什么是new
    function 学生(学号) {
        // 我们先建立一个临时对象,把例如学号之类需要改变的属性放进去
        // 【new做的第一件事:帮你创立一个临时对象,临时对象通过this访问】
        var 临时对象 = {}
        临时对象.学号 = 学号

        // 再把临时对象的__proto__手工绑定到学生原型
        // 【new做的第二件事:帮你自动把__proto__绑定到学生.原型】
        临时对象.__proto__ = 学生.原型

        // 【new做的第三件事:帮你return临时对象】
        return 临时对象
    }

    // 【但new只有一个要求:把学生原型改名为 学生.prototype】
    学生.原型 = {
        年级: "高一",
        所选方向: "理科班",
        上传资料: function () {/*上传资料的代码*/}
    }
那么,我们用new该怎么写?so easy。new帮你做的事,你还自己做它干嘛呢?
    function 学生(学号) {
        // 【new做的第一件事:帮你创立一个临时对象,临时对象通过this访问】
        // 所以我们不用创建临时对象了,把下面那行代码注释掉
        // var 临时对象 = {}

     // 把临时对象都改为this就好
        this.学号 = 学号

        // 再把临时对象的__proto__手工绑定到学生原型
        // 【new做的第二件事:帮你自动把__proto__绑定到学生原型】
        // 我们不用手动绑定了,注释掉
        // 临时对象.__proto__ = 学生原型

        // 【new做的第三件事:帮你return临时对象】
        // 我们不用手动return了,注释掉
        // return 临时对象
    }

    // 【但new只有一个要求:把学生原型改名为 学生.prototype】
    // new 帮了我们这么多忙,按照他的意思来呗,改了!
    学生.prototype = {
        年级: "高一",
        所选方向: "理科班",
        上传资料: function () {/*上传资料的代码*/}
    }

    var 学生们 = []
    for (var i = 0; i < 1000; i++) {
        学生们.push(new 学生(i))
我的天哪,我们的代码竟然通过new减少了这么多!!
constructor属性
    function test(id) {
        this.id = id
    }

    new test(1)

    console.log(test.prototype) // {constructor: ƒ}

使用new操作符的时候,为了记录临时对象是由哪个函数创建的,会在prototype里添加一个constructor属性,指向创建临时对象的函数
注意:如果直接给prototype赋值,则constructor属性会消失

function 学生(学号) {
        this.学号 = 学号
    }
    学生.prototype = {
        年级: "高一",
        所选方向: "理科班",
        上传资料: function () {/*上传资料的代码*/}
    }

    var 学生们 = []
    for (var i = 0; i < 1000; i++) {
        学生们.push(new 学生(i))
    }

    // 没有出现constructor属性
    console.log(学生.prototype) // {年级: "高一", 所选方向: "理科班", 上传资料: ƒ}

可以采用另一种赋值方式

    function 学生(学号) {
        this.学号 = 学号
    }

    学生.prototype.年级 = "高一"
    学生.prototype.所选方向 = "理科班"
    学生.prototype.上传资料 = function () {/*上传资料的代码*/}

    var 学生们 = []
    for (var i = 0; i < 1000; i++) {
        学生们.push(new 学生(i))
    }

    // 出现constructor属性
    console.log(学生.prototype) // {年级: "高一", 所选方向: "理科班", 上传资料: ƒ, constructor: ƒ}
总结 new的本质

new的本质其实就是一个语法糖,目的就是为了帮我们省代码

new的作用

创立一个临时对象,临时对象指向类的this

把实例__proto__绑定到类的prototype

return临时对象(也就是this)

关于new的语法糖
var a = {} 是 var a = new Object()的语法糖
var a = [] 是 var a = new Array()的语法糖
var a = funciton(){} 是 var a = new Function()的语法糖
参考

new运算符
JS 的 new 到底是干什么的?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107488.html

相关文章

  • this总结【1】—— this概览

    摘要:是什么这个单词是一个代词,所以应该是指代某些东西搞清楚的关键之处,就是要搞清楚指代了什么那么到底指代了什么呢就像你平时指着一个苹果说指着一个香蕉说同样,也会因为情况的不同而不同在中按照常规理解,的值是什么取决于函数如何被调用然而,的值是什么 1. this是什么 this这个单词是一个代词,所以this应该是 指代某些东西搞清楚this的关键之处,就是要搞清楚this指代了什么 那么t...

    MyFaith 评论0 收藏0
  • PostCSS自学笔记(二)【外篇二】

    摘要:之前有研究过做过假设,在插件列表中,的插件执行顺序自上而下,一切看起来似乎是没有任何问题的。再有摘自深入设计摘自写的姿势这两张图则应该是说明了我之前的假设,插件中的执行顺序自上而下。先来看看一片来自的这段会不会跟这个有关呢,我先埋个伏笔。 图解PostCSS的插件执行顺序 文章其实是一系列的早就写完了. 才发现忘了发在SegmentFault上面, 最早发布于https://gitee...

    FleyX 评论0 收藏0
  • 外篇2-基本规范、注释、static关键字、import关键字

    摘要:今日份重点命名规范注释关键字关键字总结命名规范规范的包名名字管理是所有编程语言都必须重视的一个问题。比如说百度,其域名为,那么其对应的应用的包名前缀就应该为。是谁这么大牌总结本文主要介绍了中的命名规范注解关键字关键字等内容。 欢迎关注我的微信公众号,共同打牢Java的基础,向着远方进击 showImg(https://segmentfault.com/img/bVboaBO?w=129...

    codecraft 评论0 收藏0
  • React Native填坑之旅--class(外篇

    摘要:构造函数定义侦探类作为例子。里的既是类的定义,也是构造函数。在构造函数中定义的实例方法和属性在每一个实例中都会保留一份,而在原型中定义的实例方法和属性是全部实例只有一份。 无论React还是RN都已经迈入了ES6的时代,甚至凭借Babel的支持都进入了ES7。ES6内容很多,本文主要讲解类相关的内容。 构造函数 定义侦探类作为例子。 ES5的类是如何定义的。 function ES5D...

    TwIStOy 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<