【前端谈设计】原型模式

170 阅读6分钟

前言

JS为什么会抛弃类,而去选择原型喃? 在你学习JS原型的过程中,有没有思考过这样的问题喃?

如果有,那先恭喜你,因为你已经从语法层级跨越到设计的层级啦!!!

如果没有,也不用气馁,因为看完这篇文章,你就会用另外一种角度思考原型啦!!!

img

先不着急进入正题,先来亿点点知识自测一下

  • JS是面向对象语言嘛?
  • JS中真的有类嘛?
  • JS中如何生成一个新对象喃?

现在,每个人可能都已经有了自己认为正确答案,那就带着答案看文章吧!!!

原型模式

用一个已经存在的对象作为原型对象,通过复制该原型对象来创建一个和原型对象相同或者相似的新对象

原型模式也可以称为克隆模式,克隆已存在的对象产生新的对象,被克隆的对象称为新的对象的原型

注:为了方便区分两个对象,所以我在这里把已经存在的对象称为旧对象,而新产生的对象称为新对象

image-20230222211553200

比如吸血鬼家族,如果想家族人数壮大,就必须先让老吸血鬼去感染人类,产生新吸血鬼,并且老吸血鬼也就是新吸血鬼的老师(原型)

在JAVA、C++等类语言中,会在类上暴露一个Clone方法,用于使用者克隆出新对象,而在JS中,原型模式是与生俱来的特性

类与原型

无论是类还是原型,核心都是面向对象,如何去创建对象?如何去使用对象?这也是两种设计共同要去思考的问题!!!

类还是原型都可以满足面向对象三要素:封装、继承、多态,并且在继承方面,原型比类更适合灵活多变的语言

由类创建的对象,更注重稳定和规则,就比如根据图纸造机器人,造出来的每个机器人都是一模一样的,没有独立思想的能力,不会出现机器人叛乱的情况,如果想给机器人添加新功能,那就必须去修改造机器人的图纸。而由原型创建的对象,更注重动态和修改,就比如被吸血鬼感染的人类,还会保留自己的意识,也还可以谈恋爱、撩妹妹什么的

JAVA语言就是基于类实现了面向对象,JAVA中的对象都要以类为原则,不能违背类的设定,这样的好处就是在JAVA服务于大型项目时,不容易出现意想不到的错误:规则==秩序;而JS是基于原型实现了面向对象,在使用JS的过程中,不用预先定义好类,对象也可以随时建造,随时增添属性,这就是动态,JS设计的初衷用于网页提交表单,动态的设计能够快速完成简单的任务:动态==快速

原型还有一个优势,就是在创建重复复杂对象(DOM节点)时,消耗的内存比类小,速度也比类快,你想想吸血鬼感染人类是不是要比造机器人快

JS中的原型

基于原型的面向对象是没有类的概念,对象的创建也是靠已有对象的克隆

原型模式的克隆方式分为两种:

  1. 切实的克隆旧对象,生成一个完全独立的新对象,从此两个对象互不干扰
  2. 并不是真正的克隆旧对象,而是使得新对象持有一个旧对象的引用,让新对象能够访问到新对象

显然,JS采用了第二种原型克隆的方式,这里可以通过Object.create实现对象克隆

let o = {
    name: "李白"
}
let p = Object.create(o)
p.age = 18
console.log(p);

image-20230222222016958

p对象可以动态的添加新的属性,完全不用按照任何规则,自己就是规则

p对象不仅有属于自己属性age,还可以访问到属于o对象的属性name。这里只有p对象o对象两个对象,如果现在p对象被克隆生成一个t对象

image-20230222222624787

这也就形成了常说的原型链,原型链的意见就是当访问t对象的一个属性时,如果访问不到就会沿着原型链搜查链上的所有对象

  • 对象的私有字段 [[prototype]]就是对象的原型对象
  • 读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止

设计败笔

如果是JS完全按照基于原型的设计面向对象,也就会像上面的原型链一样简单,但是学过JS原型的朋友应该都知道,JS中还有new构造函数class的语法,这些语法看一眼就让人联想到JAVA中的类,这也不怪大家误解,我觉得JS的缔造者也不知道自己想要什么!如果你给后端介绍前端的原型,你告诉他:通过new调用的函数被称构造函数,构造函数可以返回一个对象,他就会立马反驳你,这构造函数不就是类嘛。

这里还要提一句:newclass只是语法糖,通过这些语法糖创建对象的本质还是通过对象克隆实现创建对象,所以JS还是基于原型的设计面向对象,这些语法糖的产生,只是为了去模仿类的面向对象

JS诞生情况:

  1. 设计时间短,只有10天
  2. 当时JAVA热度如日中天,为了营销,所以选择抄JAVA的类,但是作者又抄了self的原型,最后就造成了现在的基于类的原型语言

我看到过这样一句对象JS的评论:好的设计都是抄袭过来的,不好的设计都是自己想的,又好笑又具有道理

正因为原型和类都要,留下许多遗留问题,比如函数既可以当函数被调用,也可以被当做类被实例化,函数被赋了多重思想。直到ES6提出class语法,才缓解了函数的问题

在网上有很多人讨论,JS的原型应不应该模仿类?这都已经模仿了,其实也没必要后悔了!!

至于类和原型如何选择:我个人认为两者可以共存,但是不适合共用,如果你喜欢原型,你就通过Object.create创建对象,如果你喜欢类,就用newclass语法创建对象,千万不要一会Object.create一会class

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 21 天