摘要:面向对象之四创建特定对象的语法糖个人学习笔记分享为了讲清楚面向对象实际上是需要几个前置知识的。于是之父创造了这个语法糖。
JS面向对象之四 【new】 (创建特定对象的语法糖)
个人学习笔记分享
为了讲清楚面向对象,实际上是需要几个前置知识的。
包括前面的几篇文章【原型链】 【this】 和今天要说的【new】
还是先说结论: new只是一个语法糖,这个语法糖被设计出来,使用场景是批量创建对象
从场景说起: 假设这个世界没有new,我们如何批量创建一百个士兵obj?通篇文章,我们都是在实现这个需求,不断优化,最终实现一个new语法糖。
类似这样的士兵ojb
var 士兵 = { ID: 1, 兵种: "美国大兵", 攻击力: 5, 生命值: 42, 攻击: function(){console.log("攻击")}, 防御: function(){console.log("防御")}, 死亡: function(){console.log("死亡")}, }版本v1.0: 最直觉的方法当然是循环100次
var 士兵 var 士兵们 = [] for (var i = 0 ; i < 100 ; i++){ 士兵 = { ID: 1, 兵种: "美国大兵", 攻击力: 5, 生命值: 42, 攻击: function(){console.log("攻击")}, 防御: function(){console.log("防御")}, 死亡: function(){console.log("死亡")}, } 士兵们.push(士兵) } 最终我们得到了array士兵们
根据第二篇文章 JS面向对象之二【原型链】(对象和对象的关系).md),我们知道这里的攻击、防御、死亡都是匿名函数,在内存空间都会占据空间。
这些函数的内容完全相同,300个匿名函数却要占用300个内存空间,十分浪费。
自然地,我们就想到JS的原型链就是专门解决这个问题的,我们把共同的属性放到__proto__里,所有士兵的__proto__都指向相同的内存空间。
版本v1.1: 优化内存空间,使用__proto__保存共同的属性var 士兵 var 士兵们 = [] for (var i=0 ; i<100 ; i++){ 士兵 = { ID: 1, 生命值: 42 } 士兵.__proto__ = 士兵共 士兵们.push(士兵) } var 士兵共 = { 兵种: "美国大兵", // 如果兵种和攻击力也是一样的话,那么也需要放到__proto__里去共享 攻击力: 5, 攻击: function(){console.log("攻击")}, 防御: function(){console.log("防御")}, 死亡: function(){console.log("死亡")}, } 最终依旧得到了array士兵们
(以上代码可粘贴运行)
现在我们省掉了之前297个匿名函数所占用的空间。让他们的__proto__都指向"士兵共"这个内容空间就OK了
var soldier var soldiers= [] for (var i=0 ; i<100 ; i++){ soldier = { ID: 1, 生命值: 42 } soldier.__proto__ = soldierCommon soldiers.push(soldier) } var soldierCommon = { 兵种: "美国大兵", // 如果兵种和攻击力也是一样的话,那么也需要放到__proto__里去共享 攻击力: 5, 攻击: function(){console.log("攻击")}, 防御: function(){console.log("防御")}, 死亡: function(){console.log("死亡")}, }版本v2.0: 封装成createSoldier函数,避免意大利式面条,让代码更加集中。
var soldierCommon = { 兵种: "美国大兵", // 如果兵种和攻击力也是一样的话,那么也需要放到__proto__里去共享 攻击力: 5, 攻击: function(){console.log("攻击")}, 防御: function(){console.log("防御")}, 死亡: function(){console.log("死亡")}, } function createSoldier(){ var obj = { ID: 1, 生命值: 42 } obj.__proto__ = soldierCommon return obj } ------分割线以上是创建构造函数, 分割线以下,是使用构造函数------ var soldiers = [] for(var i = 0 ;i<100 ; i++){ soldiers.push(createSoldier()) }版本v2.1: 让开发者一眼就知道soldierCommon和createSoldier是有关系的,让代码进一步集中
// 让soldierCommon成为构造函数的属性(因为函数也是obj,当然可以有属性) // soldierCommon可以改叫xxx,只是一个名字,只是为了让这个对象和createSoldier产生联系 createSoldier.xxx = { 兵种: "美国大兵", 攻击力: 5, 攻击: function(){console.log("攻击")}, 防御: function(){console.log("防御")}, 死亡: function(){console.log("死亡")}, } function createSoldier(){ var obj = { ID: 1, 生命值: 42 } obj.__proto__ = createSoldier.xxx return obj } ------分割线以上是创建构造函数, 分割线以下,是使用构造函数------ var soldiers = [] for(var i = 0 ;i<100 ; i++){ soldiers.push(createSoldier()) }
那么现在还可以优化吗? 上面这段代码,唯一的缺点就是xxx不太容易理解。
所以,JS之父为了容易理解将这个xxx,也就是共有属性的引用,统一叫做prototype,虽然prototype对于中国人来说依旧不好理解。
版本v2.2: 完美的代码,将xxx改为prototype (只是一个名字,但是JS之父将它命名成了prototype)createSoldier.prototype = { 兵种: "美国大兵", 攻击力: 5, 攻击: function(){console.log("攻击")}, 防御: function(){console.log("防御")}, 死亡: function(){console.log("死亡")}, } function createSoldier(){ var obj = { ID: 1, 生命值: 42 } obj.__proto__ = createSoldier.prototype return obj } ------分割线以上是创建构造函数, 分割线以下,是使用构造函数------ var soldiers = [] for(var i = 0 ;i<100 ; i++){ soldiers.push(createSoldier()) }
好了,仔细看看上面的代码,还有什么可优化的地方吗?
内存、变量名、代码集中都做了优化,也就是范例式的代码。
那么,JS之父也认为这个就是最好的代码,他希望,当JS开发者们需要批量创建特定对象时,都这样写。
于是,JS之父创造了new这个语法糖。
版本v3.0 让我们看看JS之父创造的new语法糖function Soldier(){ this.ID = 1 this.生命值 = 42 } Soldier.prototype.兵种 = "美国大兵" Soldier.prototype.攻击力 = "65 Soldier.prototype.攻击 = function(){console.log("攻击")} Soldier.prototype.防御 = function(){console.log("防御")} Soldier.prototype.死亡 = function(){console.log("死亡")} ------分割线以上是创建构造函数, 分割线以下,是使用构造函数------ var soldiers = [] for(var i = 0 ;i<100 ; i++){ soldiers.push( new Soldier()) }对比v2.2 和 v3.0 这两个版本,就是new帮我们做的事情。
v2.2 没有使用new语法的函数createSoldier
function createSoldier(){ var obj = { ID: 1, 生命值: 42 } obj.__proto__ = createSoldier.prototype return obj }
v3.0 使用了new语法的函数Soldier
function Soldier(){ // this = {} JS偷偷做的第1个事情 // this.__proto__ = Soldier.prototype JS偷偷做的第2个事情 this.ID = 1 this.生命值 = 42 // return this JS偷偷做的第3个事情 } Soldier.prototype = { JS偷偷给Soldier.prototype加了个constructor属性 constructor : Soldier }
对比一下,当你使用new语法,JS之父帮你做了什么事情
第一 , JS帮你在函数里创建了一个this空对象,并且这个this指向是构造函数的实例
第二 , JS帮你让this的__proto__ 指向 构造函数的原型,也就是prototype
第三 , JS帮你偷偷return了 this
第四 , JS偷偷给Soldier.prototype加了个constructor属性
其他注意事项
构造函数首字母大写,习惯写法
__proto__不能在生产环境出现,因为会严重影响性能
用new调用的时候,也没法用call了,因为JS之父帮你call了,this的指向就像你见到的指向实例
所以使用了new,就可以让代码没有废话。
你只需要设置对象的自有属性和共有属性。
JS帮你创建空对象,帮你搞定this指向,帮你改变this的__proto__,帮你return this
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/92407.html
摘要:首先为了模拟类创建对象的功能搞出了构造函数。也就是名字肤色肤色这里是继承里的自有属性生命值这里继承的共有属性的方法攻击力兵种美国大兵攻击防御死亡肤色 JS面向对象之五 【继承】 我们已经准备了很多前置知识,包括 原型链,对象和对象之间的关系 this,对象和函数之间的关系 new, 用函数批量创建特定的对象的语法糖 JS面向对象的前世今生 我们说,面向对象是一种写代码的套路。因为如...
摘要:一面向对象概念面向对象就是使用对象。因此在构造函数中表示刚刚创建出来的对象。在构造函数中利用对象的动态特性为其对象添加成员。 一、面向对象 1.1 概念 面向对象就是使用对象。面向对象开发就是使用对象开发。 面向过程就是用过程的方式进行开发。面向对象是对面向过程的封装。 1.2 三大特性 抽象性所谓的抽象性就是:如果需要一个对象描述数据,需要抽取这个对象的核心数据 提出需要的核心...
摘要:如果你已经对机制已有了解,但是由于两者对象机制的巨大本质差异,对它和构造函数,实例对象的关系仍有疑惑,本文或许可以解答你的问题。所有的原型对象都会自动获得一个属性,这个属性的值是指向原型所在的构造函数的指针。 帮助面向对象开发者理解关于JavaScript对象机制 本文是以一个熟悉OO语言的开发者视角,来解释JavaScript中的对象。 对于不了解JavaScript 语言,尤其是习...
摘要:前言我们在深入浅出面向对象和原型概念篇在这篇文章中了解到了如何使用解决重复创建浪费内存的问题,其中的关键就是,那么这篇文章让我们来重新了解的前世今生一个苦逼年级主任的故事开学啦高一年级主任龚主任需要为全年级每一位理科班新生录入学号并为每一位 前言 我们在深入浅出面向对象和原型【概念篇2】在这篇文章中了解到了如何使用new Function解决重复创建浪费内存的问题,其中的关键就是new...
摘要:为啥我要自己实现一个语法糖为什么要自己实现一个语法糖呢因为之前对于里的语法糖一直是理论理解但是并亲自尝试实现过。直到有一天在头条的面试中我聊了摸着自己的良心说我可以实现一个语法糖面试官嗯那你实现一个吧。我们知道构造函数一般是不写的。 为啥我要自己实现一个new语法糖? 为什么要自己实现一个new语法糖呢? 因为之前对于JS里的new语法糖一直是理论理解,但是并亲自尝试实现过。 直到有一...
阅读 3621·2021-11-23 09:51
阅读 1502·2021-11-04 16:08
阅读 3561·2021-09-02 09:54
阅读 3628·2019-08-30 15:55
阅读 2610·2019-08-30 15:54
阅读 970·2019-08-29 16:30
阅读 2057·2019-08-29 16:15
阅读 2330·2019-08-29 14:05