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

智数云翼客
• 阅读 1728

前言

我们在深入浅出面向对象和原型【概念篇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的作用

  1. 创立一个临时对象,临时对象指向类的this
  2. 把实例__proto__绑定到类的prototype
  3. 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 到底是干什么的?

点赞
收藏
评论区
推荐文章
徐小夕 徐小夕
4年前
如何用不到200行代码写一款属于自己的js类库
前言JavaScript的核心是支持面向对象的,同时它也提供了强大灵活的OOP语言能力。本文将使用面向对象的方式,来教大家用原生js写出一个类似jQuery这样的类库。我们将会学到如下知识点:闭包:减少变量污染,缩短变量查找范围自执行函数在对象中的运用extend的实现原理如何实现跨浏览器的事件监听原型链与继承接下来我会对类库
Souleigh ✨ Souleigh ✨
4年前
Js中 constructor, prototype, __proto__ 详解
本文为了解决以下问题:__proto__(实际原型)和prototype(原型属性)不一样!!!constructor属性(原型对象中包含这个属性,实例当中也同样会继承这个属性)prototype属性(constructor.prototype原型对象)__proto__属性(实例指向原型对象的指针)<br/首先弄清楚几个概念:<br/
Stella981 Stella981
3年前
JavaScript prototype原型用法
JavaScript对象原型所有JavaScript对象都从原型继承属性和方法。<!DOCTYPEhtml<html<metacharset"utf8"<titlejs</title<body<h2JavaScript对象</h2
Stella981 Stella981
3年前
JavaScript学习总结(十七)——Javascript原型链的原理
一、JavaScript原型链ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。在JavaScript中,用__proto__属性来表示一个对象的原型链。当查找一个对象的属性时,JavaScript会向上遍历原型
Stella981 Stella981
3年前
ES6 系列之 Babel 是如何编译 Class 的(上)
_摘要:_ 前言在了解Babel是如何编译class前,我们先看看ES6的class和ES5的构造函数是如何对应的。毕竟,ES6的class可以看作一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。constructorES6中:\
Stella981 Stella981
3年前
Javascript 是如何体现继承的 ?
js继承的概念js里常用的如下两种继承方式:原型链继承(对象间的继承)类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。所以,要想实现继承,可以用js的原型prototype机制或者用apply和call方法去实现在面向对象的语言中,我们使用类来创建一个自定义对象
Wesley13 Wesley13
3年前
JS原型、原型链深入理解
原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有”prototype”属性,函数对象有”prototype”属性,原型对象有”constructor”属性。原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有”prototype”属性,函数对象有”prototype”属性,原型对
Stella981 Stella981
3年前
JavaScript面向对象编程的15种设计模式
在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”。在JavaScript中并没有类这种概念,面向对象编程不是基于类,而是基于原型去面向对象编程,JS中的函数属于一等对象,而基于JS中闭包与弱类型等特性,在实现一些设计模式的方式上与众不同。ps:本文之讲述面向对象编程的设计模式策略,JavaScript原型的基础请参考阮一峰面向
Wesley13 Wesley13
3年前
ES6面向对象
ES6面向对象js中的面向对象functionUser(name,age){this.namename;//定义属性this.ageage;}//通过原型添加方法User.prototype.showNamefuncti
Wesley13 Wesley13
3年前
Java面试参考指南(一)
Java面向对象相关概念Java是一种基于面向对象概念的编程语言,使用高度抽象化来解决现实世界的问题。    面向对象的方法将现实世界中的对象进行概念化,以便于在应用之间进行重用。例如:椅子、风扇、狗和电脑等。Java里的类(Class)是一个蓝图、模板,或者称之为原型,它定义了同一类事物的相同属性和行为。实例(Instan
Wesley13 Wesley13
3年前
Java原型模式
原型模式  原型模式也称克隆模式。原型模式jianmingzhiyi,就是先创造出一个原型,然后通过类似于Java中的clone方法,对对象的拷贝,克隆类似于new,但是不同于new。new创造出来的对象采用的是默认值。克隆出来的对象与原型对象相同,同时不会影响原型对象,然后在修改克隆出来的对象。实现  继承Cloneable接口,重写cl