【前端谈设计】构造器模式

98 阅读5分钟

前言

原型模式的文章中聊过,JS面向对象的设计理念是基于原型,也就是说在JS中是不应该存在基于类的面向对象,并且文章末尾提到了我对JS中构造器的设计态度,构造器是JS语言设计中的一大败笔,其实我觉得可能不仅仅只有我会这样想,就连JS的设计者可能也是这么想的,这篇文章就来讲一讲为什么会出现这种情况!!!

构造器模式

当新建对象的内存被分配后,用来初始化该对象的一个特殊函数,用于创建特定类型的对象

构造器的主要功能是接受请求参数,在新建对象时,给新对象添加属性和方法。按照功能实现来讲,构建器也可以被称为建造者模式

function Build(obj, name, age) {
    this.name = name
    this.age = age
}
let obj = Object.create(null)
obj = Build(obj, "李白", 18)

对象创建和构建过程是分开的,Build函数被称为构建器。

败笔原因

在JS中,可以通过使用new关键字省去创建对象的步凑

function Build(name, age) {
    this.name = name
    this.age = age
}
let obj = new Build("李白", 18)

这里的效果和上面是一样的,而且JS中的所以函数都可以通过new调用,这是因为在JS函数都有一个隐藏属性[[class]],也就说明了JS中所以的函数都能叫构造函数(包括ArrayObjectNumber等内置函数)。其实在这里有很多人都想去区分开普通函数和构造函数,提出了被new调用的函数是构造函数,而没有被new调用的函数是普通函数,但是我觉得这些观念都是掩耳盗铃,因为被不被new调用,还不是看开发中心情,就像车子一样,无论开不开都要被称为车。要怪就怪JS的设计,为什么允许new调用普通函数,而不是专门为构造函数添加特殊标识

在JAVA语言中,通过new调用的主体被称为类,而且JAVA中的类和JS中的构造函数完成的业务还是一样的,都是实例对象、构建对象。

这里就不得不提出一个疑问了,JS中的构造函数就是JS中的类吗?如果说是,就显然和JS原型的设计背道而驰了,JS是基于原型的面向对象语言,而不是基于类的面向对象语言,并且在原型模式中提过JS一切皆对象,克隆已有对象产生新对象,那JS不是类又是什么喃?

这也就是JS构造器设计的败笔之一:通过new构造函数创建新对象,当你想去给JAVA程序员介绍构造器时,你说构造器可以通过new调用而产生新的对象,他的第一反应接受,这不就是类吗?看吧,产生分歧了吧,JS设计者想在原型语言中保留类的概念,就像想在水中放入油漆一样,不仅突兀,还不利于用户理解

构造器的本质

前面讲了new调用的构造函数可以省去创建对象的步凑,那就来看看如何省去的吧

function _New(Constructor) {
    // 1.从 Object.prototype 上克隆一个空的对象
    var person = Object.create(Object.prototype);
    // 2.取出构造函数
    var constructor = [].shift.call(arguments);
    // 3.继承构造函数的原型
    person._proto_ = constructor.prototype;
    // 4.为新建的对象调用构造函数,生成内部属性
    let ret = constructor.apply(person, arguments);
    // 5.返回对象
    return typeof ret === 'object' ? ret : person;
}
​
function Build(obj, name, age) {
    this.name = name
    this.age = age
}
let obj = _new(Build, "李白", 18)

new包含三个功能:

  1. 通过克隆生成新对象
  2. 将构造器的prototype属性设置为新对象的原型
  3. 给新对象添加属性和方法

其实不难发现,new调用构造函数创建对象的底层逻辑还是使用的是对象克隆,只是通过new掩盖了底层的真相。

image-20230223221110146

结合原型模式和构造器模式,我们可以做出完整JS原型链

未命名文件 (1)

是不是很简单喃?就是通过图也可以得出一个结论:构造器本职工作是储存原型对象,用于设置新对象的原型

JS一切皆对象,那就说明构造器也是对象,原型模式中讲过克隆已有才能对象产生新对象,那构造函器的也应该有原型对象

image-20230223215003029

image-20230223223050804

看了这种原型图,我已经无力吐槽JS设计者啦,你们帮我吐槽吧!!!

离谱!离大谱!

  • Funtion构造器制造了创建了自己,因为Funtion构造器即是对象也是函数
  • Funtion构造器prototype属性和__proto_属性一样
  • Funtion构造器创建了Object构造器,而Object构造器创建了Funtion构造器prototype属性

如果不引入,构造器的概念是不是就没这么多麻烦事事情了!!!

总结

我认为构造器是JS败笔的原因:

  • 构造器不应该和函数共用一种定义方式
  • 不应该使用new调用构造器
  • 不应该把Funtion构造器归入对象管理

言归正传,虽说JS有如此多的败笔,但还是热爱JS,在我们的编码中,你可以选择不用构造器,而选择用原型层次的函数Object.createObject.getPrototypeOfObject.setPrototypeOf

而对于构造器,在ES6中有一个好消息就是,提出class概念,虽然没能解决原型的问题,但是起码解决了构造器不应该和函数共用一种定义方式

我就说嘛,连设计者都看不下去了!!!!

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 23 天,点击查看活动详情