前言
在原型模式的文章中聊过,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中所以的函数都能叫构造函数(包括Array
、Object
、Number
等内置函数)。其实在这里有很多人都想去区分开普通函数和构造函数,提出了被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
包含三个功能:
- 通过克隆生成新对象
- 将构造器的
prototype
属性设置为新对象的原型 - 给新对象添加属性和方法
其实不难发现,new
调用构造函数创建对象的底层逻辑还是使用的是对象克隆,只是通过new
掩盖了底层的真相。
结合原型模式和构造器模式,我们可以做出完整JS原型链
是不是很简单喃?就是通过图也可以得出一个结论:构造器本职工作是储存原型对象,用于设置新对象的原型
在JS一切皆对象,那就说明构造器也是对象,原型模式中讲过克隆已有才能对象产生新对象,那构造函器的也应该有原型对象
看了这种原型图,我已经无力吐槽JS设计者啦,你们帮我吐槽吧!!!
离谱!离大谱!
Funtion构造器
制造了创建了自己,因为Funtion构造器
即是对象也是函数Funtion构造器
的prototype
属性和__proto_
属性一样Funtion构造器
创建了Object构造器
,而Object构造器
创建了Funtion构造器
的prototype
属性
如果不引入,构造器的概念是不是就没这么多麻烦事事情了!!!
总结
我认为构造器是JS败笔的原因:
- 构造器不应该和函数共用一种定义方式
- 不应该使用
new
调用构造器 - 不应该把
Funtion构造器
归入对象管理
言归正传,虽说JS有如此多的败笔,但还是热爱JS,在我们的编码中,你可以选择不用构造器,而选择用原型层次的函数Object.create
、Object.getPrototypeOf
、Object.setPrototypeOf
。
而对于构造器,在ES6中有一个好消息就是,提出class
概念,虽然没能解决原型的问题,但是起码解决了构造器不应该和函数共用一种定义方式
我就说嘛,连设计者都看不下去了!!!!
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 23 天,点击查看活动详情