创建型:原型模式
原型模式不仅是一种设计模式,还是一种编程规范,是js面向对象系统实现的根基。
在原型模式下,要创建一个对象时,会先找到一个对象作为原型,然后通过 克隆原型 的方式来创建一个与原型一样的对象。
JS中的类,本质上是 原型继承的语法糖。
ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。
类语法不会为 JavaScript 引入新的面向对象的继承模型。 ——MDN
class Dog{ constructor(name,age){ this.name = name; this.age = age; } eat(){ console.log("我是吃吃吃") } } // 完全就等价于下面这个构造函数 function Dog1(name,age){ this.name = name; this.age = age; } Dog.prototype.eat = function(){ console.log("我是吃吃吃1") }
所以说JS语言本身就是原型模式。JS本身类型就比较模糊,不存在类型耦合的问题,所以没必要强行把原型模式当作设计模式去理解,它作为一种编程规范来讨论更合适。
原型编程规范的核心思想就是 利用实例来描述对象,用实例作为定义对象和继承的基础。
在JS中,原型编程规范的体现就是基于原型链的继承。
原型
在JS中,每一个构造函数都有一个 prototype 属性,它指向构造函数的原型对象,这个原型对象中有一个 constructor 属性指回构造函数,每一个实例都有一个
__proto__属性,当使用构造函数创建实例时,实例的 __proto__ 属性就会指向构造函数的原型对象。
// 创建一个Dog构造函数 function Dog(name, age) { this.name = name this.age = age } Dog.prototype.eat = function() { console.log('吃吃吃') } // 使用Dog构造函数创建dog实例 const dog = new Dog('旺财', 4)
上面这段代码几个实体之间存在的关系
原型链
// 调用上面的方法 dog.eat() // 输出"吃吃吃" dog.toString() // 输出"[object Object]"
在 dog 实例中明明没有手动定义,eat和toString 方法,但是还是可以调用成功。这是因为当我们试图访问一个 JS 实例的属性/方法时,首先搜索实例本身,如果实例没有定义对应的属性/方法时,它会转而去搜索实例的原型对象;如果原型对象也没有找到,他就去搜索原型对象的原型对象,这个搜索轨迹,就是原型链。
eat方法和toString方法的调用过程为例,它的搜索过程如下图:
上图彼此相连的 prototype,就组成一个原型链。
几乎所有的JS中的对象都是位于原型链顶端的Object 的实例,除了object.prototype。