javascript中的继承与实现


javascript中的继承与实现

文章插图
 
 
继承
  • js中的继承一般分为三部分:原型属性继承、静态属性继承、实例属性继承,一个原型上面定义的方法一般都是基于其实例的用途来定义的,也就是说,原型的方法应该是实例经常用到的通用方法,而构造器方法一般是特定情况下可能会用到的方法,可按需调用,原型方法只能供其实例来使用
  • 继承可以让原型链丰富,根据需求定制不同的原型链,不会存在内存浪费的情况,原型只会保留一份,用到的时候调用就行,还能节省空间
原型继承 
javascript中的继承与实现

文章插图
 
 
  • 可以看出原型一般是一些共有的特性,实例是特有的特性,继承的越多越具体,原型链的最顶端是最抽象的,越底端越具体,这样一来我们可以根据需求在恰当位置继承来实现个性化的定制属性,统一而又有多样化
原型之间的继承function Parent(){} // 定义父类构造器function Children(){} // 定义子类构造器let ChildPrototype = Children.prototype // 构造器原型let ChildPrototypeProto = Children.prototype.__proto__ // 构造器原型的对象原型// 方法一ChildPrototypeProto = Parent.prototype // 父类构造器原型作为子类构造器原型(ChildPrototype)的对象原型(ChildPrototypeProto)//方法二ChildPrototype = Object.create(Parent.prototype) // Object.create返回一个对象,其__proto__指向传入的参数,也就实现返回的对象继承参数对象//方法三Object.setPrototypeOf(ChildPrototype, Parent.prototype) // 直接设置参数1的原型(__proto__)为参数2复制代码以上仅实现了原型之间的继承
静态属性继承
  • 静态属性的继承,意味着父构造器中定义的静态属性,在子构造器中可以直接调用 。不仅实例可以通过对象原型实现继承,构造器也可以通过对象原型继承 。之前提到过函数有prototype与__proto__,其中prototype是给实例用的,而__proto__是给自己用的 。
  • 默认的构造函数的对象原型都指向原始函数构造器原型(即Function.prototype),可以理解所有函数都是由原始函数构造器生成
  • 通过构造函数自身的对象原型(__proto__),来实现静态属性继承
function Parent() {} // 定义父构造函数function Children() {} //定义子构造函数// 定义父构造函数的静态方法Parent.foo = function () {console.log(this.name)}// 方法一Children.__proto__ = Parent // 子构造函数的对象原型指向父构造函数,也就实现继承// 方法二Object.setPrototypeOf(Children, Parent) // 同原型继承console.log(Children.foo) // function(){ console.log(this.name) } ,实现继承复制代码以上即为构造函数之间通过对象原型继承静态属性,注:函数也是对象
实例属性继承
  • 实例自带的属性是由构造函数实例化时默认生成的,那么要实现实例属性的继承,势必要实现子构造函数中调用父构造函数,这样才能实现子构造函数实例化出来的对象也具备父构造函数给予的默认属性
  • 在class语法糖的constructor中的super()函数就是实现这个继承
// 定义父构造函数function Parent(name) {this.name = name}//定义子构造函数function Children(name,age) {Parent.call(this,name)// 这里调用父构造器,实现实例属性继承this.age = age}const obj = new Children('tom', 5)console.log(obj) // {name: 'tom', age: 5} ,实现实例属性继承复制代码通过实例属性继承,可以把父构造器中默认生成的实例属性追加到子构造器实例化出来的对象上
综合以上继承,现在实现真正的继承
继承的实现
  • 通过es6的extends关键字来继承原型
  • 手动实现原型继承
// 定义父构造函数,功能:初始化实例name属性function Parent(name) {'use strict'this.name = name}// 定义父构造函数的静态方法,功能:设置调用对象的name属性Parent.setName = function setName(obj, name) {obj.name = name}// 定义父构造器原型(prototype)的方法,功能:获取调用对象的name属性Parent.prototype.getName = function getName() {return this.name}/*-----以上已定义父类的原型方法(获取name),父类静态方法(设置name),以及构造器默认初始化的属性name------*/// 定义子构造函数,功能:初始化实例age属性,以及通过父构造器初始化实例name属性function Children(name, age) {'use strict'Parent.call(this, name) // 调用父构造器,初始化name属性this.age = age // 子构造器初始化age属性}// 定义子构造函数的静态方法,功能:设置调用对象的age属性Children.setAge = function setAge(obj, age) {obj.age = age}// 原型继承// 设置Children.prototype['[[Prototype]]']= Parent.prototype,此处的'[[Prototype]]'与设置__proto__相同Children.prototype = Object.create(Parent.prototype)// 注意此处原型继承之后,不带有constructor属性,应该手动指明为ChildrenObject.defineProperty(Children.prototype, 'constructor', {value: Children,writable: true, // 可写enumerable: false, // 不可枚举configurable: true, // 可配置})//以上2句可以直接写成一句/*Children.prototype = Object.create(Parent.prototype, {constructor: {value: Children,writable: true, // 可写enumerable: false, // 不可枚举configurable: true, // 可配置}})*/// 由于子构造器原型方法必须在继承之后再定义,否则会被继承覆盖// 定义子构造器原型(prototype)的方法,功能:获取调用对象的age属性Children.prototype.getAge = function getAge() {return this.age}// 构造函数(继承静态属性)继承// 设置Children.__proto__ = Parent,注意此处不能使用Children = Object.create(Parent),因为Object.create返回的是一个对象不能替换构造函数Object.setPrototypeOf(Children, Parent)// 测试父级const obj = new Parent('tom') // 实例化父级实例console.log(obj.getName()) // tomParent.setName(obj, 'jerry') // 通过父级静态方法设置nameconsole.log(obj.getName()) // jerryconsole.log(obj instanceof Parent) // true// 测试子级const obj1 = new Children(null, 5) // 实例化子级实例console.log(obj1.getAge()) // 5Children.setAge(obj1, 8) // 通过子级静态方法设置ageconsole.log(obj1.getAge()) // 8console.log(obj1 instanceof Parent) // trueconsole.log(obj1 instanceof Children) // true// 完整测试继承const test = new Children('tom', 5) // 实例化子级实例,name='tom',age=5console.log(test.getName()) // tomParent.setName(test, 'jerry') // 通过父级静态方法设置name=jerryconsole.log(test.getName()) // jerryconsole.log(test.getAge()) // 5Children.setAge(test, 8) // 通过子级静态方法设置age=8console.log(test.getAge()) // 8class P {constructor(name) {this.name = name}static setName(obj, name) {obj.name = name}getName() {return this.name}}class C extends P {constructor(name, age) {super(name)this.age = age}static setAge(obj, age) {obj.age = age}getAge() {return this.age}}// 这里就不带测试了,可以自行验证,比对一下有什么区别console.dir(Children)console.dir(C)复制代码


推荐阅读