如果没有继承
假如要写2个构造函数,他们有可能会有重复代码,如果没有继承,这些都要重新写一次
对象的继承方案
继承方案一:原型链继承
说明
//父类:公共属性和方法function Person () {this.name = "why";this.age = 18;this.friends = []}Person.prototype.eating = function () {console.log(this.name + "eating~")}//子类:特有属性和方法function Student () {this.sno = 111;}Student.prototype = new Person()Student.prototype.studying = function () {console.log(this.name + "studying~")};let stu = new Student()console.log(stu.name) // whystu.eating() // whyeating~
弊端
1、某些继承属性看不到
// 打印son,某些属性看不到console.log(stu) // Student {sno: 111}
2、公共的属性,如果值是引用类型的(对象、数组)子类会相互影响
let stu1 = new Student()let stu2 = new Student()// 直接修改实例对象上的属性,是给该实例对象添加了一个新属性stu1.name = 'kobe'console.log(stu2.name); // why// 获取引用,修改引用中的值,会相互影响stu1.friends.push('kobe')console.log(stu1.friends); // ['kobe']console.log(stu2.friends); // ['kobe']
因为都是同一个引用,stu1.friends.push() 一开始会查找stu1有没有friends这个属性,没有,然后查找原型上有没有,如果有,就往原型的数组上push,因此和自己是没有关系的。
3、不好传参数
function Student (name, sno) {this.name = namethis.sno = 111;}let stu3 = new Student('张三',112)// 这样传的话,需要修改Student这个函数,这样他的父类Person就没意义了
我们想做的应该是Person里面传参数,但是new Person( ) 的时候就会定下来(如name),这样所有后面所有的学生都有共同的名字,也不是我们想要的
原型式继承函数
这种模式要从道格拉斯·克罗克福德(Douglas Crockford,著名的前端大师,JSON的创立者)在2006年写的一篇文章说起: Prototypal Inheritance in JavaScript(在JS中使用原型式继承)
既创建一个函数,给对象设置原型:
// 原型链式继承函数// 方式一function createObject1 (o) {const newObj = {};Object.setPrototypeOf(newObj, o)return newObj}// 方式二function createObject2 (o) {function Fn () { }Fn.prototype = o;const newObj = new Fn()return newObj}let obj = {name: '张三'}const info = createObject2(obj)console.log(info);/*Fn {}[[Prototype]]: Objectname: "张三"*/console.log(info.__proto__); // {name: '张三'}// 或者用方法:Object.create( ) ,和上面功能一样的const info = Object.create(obj)console.log(info);/*{}[[Prototype]]: Objectname: "张三"*/console.log(info.__proto__);
继承方案二:借用构造函数
说明
//父类:公共属性和方法function Person (name, age, friends) {this.name = "why";this.age = 18;this.friends = []}Person.prototype.eating = function () {console.log(this.name + "eating~")}//子类:特有属性和方法function Student (name, age, friends, sno) {// call( )或apply( )Person.call(this.name, age, friends)this.sno = 111}Student.prototype = new Person()Student.prototype.studying = function () {console.log(this.name + "studying~")};
优势
一次解决方案一的三个弊端
let stu1 = new Student('why', 18, ['lilei'], 111)let stu2 = new Student('kobe', 30, ['james'], 112)console.log(stu1); // Student {name: 'why', age: 18, friends: ['lilei'], sno: 111}
弊端
- 第一个弊端:Person函数至少被调用了两次
- 第二个弊端:stu的原型对象上会多出一些属性,但是这些属性是没有存在的必要
继承方案三:寄生式继承
寄生式(Parasitic)继承是与原型式继承紧密相关的一种思想,并且同样由道格拉斯·克罗克福德(DouglasCrockford)提出和推广的
寄生式继承的思路是结合原型类继承和工厂模式的一种方式
即创建一个封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再将这个对象返回
const personObj = {running: function () {console.log("running")}}function createStudent (name) {let stu = Object.create(personObj)stu.name = namestu.studying = function () { console.log("studying~") }return stu}let stuObj = createStudent("why ")let stuObj1 = createStudent("kobe")let stuObj2 = createStudent("james")
最终继承方案四:寄生组合式继承
// 父类function Person (name, age, friends) {this.name = namethis.age = agethis.friends = friends}Person.prototype.running = function () {console.log("running~")}Person.prototype.eating = function () {console.log("eating~")}// 子类function Student (name, age, friends, sno, score) {Person.call(this, name, age, friends)this.sno = snothis.score = score}// 1.修改原型Student.prototype = Object.create(Person.prototype)// 2.修改类型,否则打印时类型是Person,因为student没有自己的constructorobject.defineProperty(Student.prototype, "constructor", {enumerable: false,configurable: true,writable: true,value: Student})// 3.再给子类定义自己的方法Student.prototype.studying = function () {console.log("studying~")}// 4.创建具体的子类对象var stu = new Student("why", 18, ["kobe"], 111, 100)// 把1、2重新封装一下,方便后面给其他不同对象继承//定义寄生式核心函数function inheritPrototype (subType, superType) {subType.prototype = object(superType.prototype)subType.prototype.constructor· = subType}// 调用inheritPrototype(Student, Person)
