在初学JS原型模式时,总是会以面向对象模式的视角去思考学习原型模式,故总是会想方设法的在JS中去寻找类、对象、接口的存在,仿佛这些特性的存在是理所当然的,内心早已经认定了面向对象模式就是原型模式,JS中也就应该有面向对象语言的特性!!!
但是现在静下心来思考一下,JS原型模式真的拥有面向对象模式的特征嘛?封装、继承、多态真的存在于JS原型模式中嘛?是不是自己都模棱两可喃~~~
这要怪只能怪JS语言为了随大流,新出来的语法糖越来越靠近面向对象的语法,比如ES6 中的class语法糖,但是这里要记住只是语法靠近面向对象的语法,但是模式的本身还是原型模式~~~
不能在面向对象语法中迷失了对原型模式的渴望,JS不能没有原型模式,就像四川菜不能缺少辣椒一样~~~
前言
对于面向对象模式和原型模式模式的对象,可以从多态、封装、继承等不同的方面入手,但是因为是弱类型语言,多态、封装在JS中表现的不是很明显,这里先主要讲继承~~~
”继承“词一出,是不是首先想到的是类继承,也就是面向对象语言的继承方式,但在JS遵守的继承是原型继承,两者区别还是很大的哦!!!
面向对象类继承
面向对象模式讲究的是类与对象,类指的是一个实体的抽象,对象就是抽象的实现,这就相当于图纸和车的概念
面向对象模式是发生在类上,通过建立类于类直接关联继承,形成一个新的类,就能够实例出不同的对象
比如车辆的设计图纸就是类,而根据图纸造出来的车就是对象,原本的图纸只能造出奥迪A3,为了造出装甲车,让车图纸继承机枪图纸,得到了一份装甲车的图纸,也就可以名正言顺的造装甲车了
车类继承了机枪类,那么机枪类就是车类的父类,通过继承之后的车类,造成的车就不是奥迪A3了,那为什么会这样喃?
因为新造出来的车不仅仅可以访问到车的属性和方法,还能够访问到机枪的方法和属性
通过java语法简单演示一下吧
class Machine {
public void shoot() {
System.out.println("开枪了");
}
}
class Car extends Machine {
}
Car car = new Car();
car.shoot();
之所以car对象能够使用shoot方法,就是因为Car类继承Machine类
在面向对象语言中还有一个基类,顾名思义就是所以类的基础,也就是所以类的父类,在java中是Object
原型模式继承
在原型模式中,没有类的概念,所有的数据都是对象,要创建一个新对象,是通过克隆已有对象产生新对象
原型继承的实现通过克隆已有对象,然后对新对象扩充,这样新对象不就拥有被克隆对象的数据,还拥有新补充的数据
比如机器人阿尔法狗下棋很厉害,现在科学家想再造一个机器人阿尔法猫,他不仅能下棋,还需要会格斗,只需要把将阿尔法狗克隆一下,制造出和阿尔法狗一样的阿尔法猫,再单独给阿尔法猫添加格斗技巧,这样阿尔法猫就可以文武双全啦
所有数据都是对象,不存在类的概念想得到一个对象,只能克隆另外一个对象
阿尔法猫是通过克隆阿尔法狗得到的,阿尔法猫拥有阿尔法狗的所有技能,阿尔法狗就是阿尔法猫原型对象
let Dog = {
chess() {
console.log("我下棋贼厉害");
}
}
let Cat = Object.create(Dog) // 克隆
Cat.combat = function () {
console.log("我打架贼厉害");
}
console.log(Cat.combat()); // 我打架贼厉害
console.log(Cat.chess()); // 我下棋贼厉害
Cat是通过克隆Dog得到的,但实际上并不是真正意义上的创建另外一个Dog对象,其实这里的克隆是指创建一个新对象,把被克隆对象当做新对象的原型对象
Object.create的可以这样实现
const myCreate = function (obj) {
let o = {}
o.__proto__ = obj
return o;
}
细心的朋友发现 let o = {},不是说需要克隆才能得到对象嘛? let o = {}克隆谁了?
JS有一个顶层对象Object.prototype,这是所以对象的最高原型对象,也可以说所以的对象都是通过Object.prototype克隆得到的,let o = {}其实就是克隆Object.prototype创建对象
克隆对象的意义是实现继承:
- 运行
Cat.chess()时,首先在Cat寻找chess方法,但是查找无果 - 继续查找
Cat的原型Dog,在Dog终于找到chess方法
那如果运行的是Cat.toString()喃,在Cat和Dog都找不到?就要继续找Dog的原型对象,也就是Object.prototype对象,上面说了呀,所以的对象都是通过Object.prototype克隆得到的,也就是所以对象的根原型都是Object.prototype
再说,万一Object.prototype都找不到喃?那就报undefinde
对象会记住它的原型,克隆谁而得到他,谁就是他的原型如果对象无法响应某个请求,它会把这个请求委托给它的原型
构造器
上面一直没讲构造器,是我觉得构造器的产生,是为了迎合面向对象语言而设计的,本质并不属于原型模式
其实看一下构造器的本质就很容易发现了
function myNew(Constructor) {
// 1.从 Object.prototype 上克隆一个空的对象
var person = new Object();
// 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;
}
这里只需要看第1步和第3步,是不是很熟悉喃?因为和Object.create使用一模一样的
所以构造器本身也就是封装了原型克隆的过程~~~
结语
想知道Object.create为什么会没落嘛?可以关注下一篇文章
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 14 天,点击查看活动详情”