JS继承的几大方法
原文链接: https://juejin.im/post/6844904098941108232
这篇是阅读文章,自己在控制台打印,记录一下,方便后期复习
原文写得很好,容易理解,对继承不太清楚的可以看看
// 一,原型链继承
// function Parent () {
// this.name = 'Parent'
// this.sex = 'boy'
// }
// Parent.prototype.getName = function () {
// console.log(this.name)
// }
// function Child () {
// this.name = 'child'
// }
// 将子类的原型对象指向父类的实例或者指向父类的原型对象
// Child.prototype = new Parent()
// Child.prototype = Parent.prototype
// var child1 = new Child()
// child1.getName()
// console.log(child1)
// 将上面的原型方法再添加一个
// Parent.prototype.getSex = function () {
// console.log(this.sex)
// }
// 使用这种方法只能继承到父类原型链上的方法,构造函数上的属性和方法将不会被继承
// Child.prototype = Parent.prototype
// 使用父实例继承,可以继承构造函数和原型对象上的方法
// Child.prototype = new Parent()
// var child1 = new Child()
// child1.getName()
// child1.getSex() // undefined
// console.log(child1)
/** --------------------------------------- */
// function Parent (name) {
// this.name = name
// this.sex = 'boy'
// this.colors = ['white', 'black']
// }
// function Child () {
// this.feature = ['cute']
// this.colors = ['white', 'red']
// }
// var parent = new Parent('parent')
// Child.prototype = parent
// Child.prototype.age = 17
// var child1 = new Child('child1')
// child1.sex = 'girl'
// child1.colors.push('yellow')
// child1.feature.push('sunshine')
// var child2 = new Child('child2')
// console.log(child1) // colors: (3) ["white", "red", "yellow"] feature: (2) ["cute", "sunshine"] sex: "girl"
// console.log(child2) // colors: (2) ["white", "red"] feature: ["cute"]
// console.log(child1.name) // parent
// console.log(child2.age) // 17
// console.log(parent) // colors: (2) ["white", "black"] name: "parent" sex: "boy" age:17
// 原型链继承缺点
// 1.子类原型新增方法和属性要放在继承之后
// 2.无法实现多继承
// 3.来自原型对象的所有属性都被共享了,如果修改原型对象的属性,所有创建的实例对象都会受到影响
// 4.创建子类时,不能向父类构造函数传参
/** --------------------------------------- */
// instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
// a instanceof B
// 实例对象a instanceof 构造函数B
// 检测a的原型链(__proto__)上是否有B.prototype,有则返回true,否则返回false。
// function Parent () {
// this.name = 'parent'
// }
// function Child () {
// this.sex = 'boy'
// }
// Child.prototype = new Parent()
// var child1 = new Child()
// console.log(child1 instanceof Child) // true
// console.log(child1 instanceof Parent) // true
// console.log(child1 instanceof Object) // true
/** --------------------------------------- */
// isPrototypeOf()的用法和instanceof相反。
// 它是用来判断指定对象object1是否存在于另一个对象object2的原型链中,是则返回true,否则返回false。
// function Parent () {
// this.name = 'parent'
// }
// function Child () {
// this.sex = 'boy'
// }
// Child.prototype = new Parent()
// var child1 = new Child()
// console.log(Child.prototype.isPrototypeOf(child1)) // true
// console.log(Parent.prototype.isPrototypeOf(child1)) // true
// console.log(Object.prototype.isPrototypeOf(child1)) // true
/** --------------------------------------- */
// 3.构造函数继承
// call接受若干个参数 apply接受一组数组 bind不会立即执行,返回函数
// function Parent (name) {
// this.name = name
// }
// function GrandParent (age) {
// this.age = age
// }
// function OtherParent (color) {
// this.color = color
// }
// function Child () {
// this.sex = 'boy'
// this.name = 'juan'
// Parent.call(this, 'child')
// this.name = 'junn'
// GrandParent.apply(this, [17, 'red'])
// }
// var child1 = new Child()
// OtherParent.bind(child1, 'yellow')()
// console.log(child1) // {sex: "boy", name: "junn", age: 17, color: "red"}
/** --------------------------------------- */
// function Parent (name, sex) {
// this.name = name
// this.sex = sex
// this.colors = ['white', 'black']
// }
// function Child (name, sex) {
// Parent.call(this, name, sex)
// }
// var child1 = new Child('child1', 'boy')
// child1.colors.push('yellow')
// var child2 = new Child('child2', 'girl')
// console.log(child1) // {name: "child1", sex: "boy", colors: Array(3)}
// console.log(child2) // {name: "child2", sex: "girl", colors: Array(2)}
// 构造继承的优点: 1.可以继承多个父类 2.修改实例不会影响其他实例 3.可以传递参数
/** --------------------------------------- */
// const arr = ['5', '6', '7', '11']
// const res = Math.max.apply(null, arr)
// console.log(res)
// function Parent (name) {
// this.name = name
// }
// Parent.prototype.getName = function () {
// console.log(this.name)
// }
// function Child () {
// this.sex = 'boy'
// Parent.call(this, 'good boy')
// }
// Child.prototype.getSex = function () {
// console.log(this.sex)
// }
// var child1 = new Child()
// console.log(child1)
// child1.getSex()
// child1.getName() // 报错
// 构造函数的缺点: 不能继承父类构造函数原型对象上的方法和属性
/** --------------------------------------- */
// function Parent (name) {
// this.name = name
// }
// function Child () {
// this.sex = 'boy'
// Parent.call(this, 'child')
// }
// var child1 = new Child()
// console.log(child1)
// console.log(child1 instanceof Child) // true
// console.log(child1 instanceof Parent) // false
// console.log(child1 instanceof Object) // true
// 通过构造函数继承的实例不是继承父类的实例,也不能使用父类原型对象上的方法
/** --------------------------------------- */
// 4.组合继承
// 组合继承就是将原型链继承与构造函数继承组合在一起,从而发挥两者之长的一种继承模式
// function Parent (name) {
// this.name = name
// }
// Parent.prototype.getName = function () {
// console.log(this.name)
// }
// function Child (name) {
// this.sex = 'boy'
// Parent.call(this, name)
// }
// Child.prototype = new Parent()
// Child.prototype.getSex = function () {
// console.log(this.sex)
// }
// var parent1 = new Parent('parent1')
// var child1 = new Child('child1')
// console.log(child1) // Child{ name: 'child1', sex: 'boy' }
// console.log(parent1)// Parent{ name: 'parent1' }
// child1.getName() // 'child1'
// child1.getSex() // 'boy'
// parent1.getName() // 'parent1'
// parent1.getSex() // Uncaught TypeError: parent1.getSex is not a function
/** --------------------------------------- */
// function Parent (name) {
// this.name = name
// }
// Parent.prototype.getName = function () {
// console.log(this.name)
// }
// function Child (name) {
// this.sex = 'boy'
// Parent.call(this, name)
// }
// Child.prototype = new Parent()
// Child.prototype.getSex = function () {
// console.log(this.sex)
// }
// var child1 = new Child('child1')
// var parent1 = new Parent('parent1')
// console.log(child1.constructor) // parent
// console.log(parent1.constructor) // parent
// 原型链继承会切断child原来的原型链结构,需要加上Child.prototype.constructor = Child修正
// constructor它是构造函数原型对象中的一个属性,正常情况下它指向的是原型对象。
// 它并不会影响任何JS内部属性,只是用来标示一下某个实例是由哪个构造函数产生的而已。
// 如果我们使用了原型链继承或者组合继承无意间修改了constructor的指向,那么出于编程习惯,我们最好将它修改为正确的构造函数。
/** --------------------------------------- */
// function Parent (name, colors) {
// this.name = name
// this.colors = colors
// }
// Parent.prototype.features = ['cute']
// function Child (name, colors) {
// this.sex = 'boy'
// Parent.apply(this, [name, colors])
// }
// Child.prototype = new Parent()
// // Child.prototype.constructor = Child
// Child.prototype.age = '17'
// var child1 = new Child('child1', ['white'])
// child1.colors.push('yellow')
// child1.features.push('sunshine')
// var child2 = new Child('child2', ['black'])
// var par = new Parent('child2', ['black'])
// console.log(child1)
// console.log(child2)
// console.log(Child.prototype)
// console.log(child1.features)
// console.log(child2.features)
// console.log(par.features)
// console.log(child1 instanceof Child)
// console.log(Child.prototype.isPrototypeOf(child1))
// console.log(child1 instanceof Parent)
/** --------------------------------------- */
// function Parent (name) {
// console.log(name) // 这里有个console.log()
// this.name = name
// }
// function Child (name) {
// Parent.call(this, name)
// }
// Child.prototype = new Parent()
// var child1 = new Child('child1')
// console.log(child1)
// console.log(Child.prototype)
// 组合继承的缺点: 1 使用组合继承时,父类构造函数会被调用两次
// 2 并且生成了两个实例,子类实例中的属性和方法会覆盖子类原型(父类实例)上的属性和方法,所以增加了不必要的内存。
/** --------------------------------------- */
// 组合继承
// 实现方式:
// 使用原型链继承来保证子类能继承到父类原型中的属性和方法
// 使用构造继承来保证子类能继承到父类的实例属性和方法
// 优点:
// 可以继承父类实例属性和方法,也能够继承父类原型属性和方法
// 弥补了原型链继承中引用属性共享的问题
// 可传参,可复用
// 缺点:
// 使用组合继承时,父类构造函数会被调用两次
// 并且生成了两个实例,子类实例中的属性和方法会覆盖子类原型(父类实例)上的属性和方法,所以增加了不必要的内存。
// constructor总结:
// constructor它是构造函数原型对象中的一个属性,正常情况下它指向的是原型对象。
// 它并不会影响任何JS内部属性,只是用来标示一下某个实例是由哪个构造函数产生的而已。
// 如果我们使用了原型链继承或者组合继承无意间修改了constructor的指向,那么出于编程习惯,我们最好将它修改为正确的构造函数。
/** --------------------------------------- */
// 5.寄生组合继承
// function Parent (name) {
// this.name = name
// }
// Parent.prototype.getName = function () {
// console.log(this.name)
// }
// function Child (name) {
// this.sex = 'boy'
// Parent.call(this, name)
// }
// // 与组合继承的区别
// Child.prototype = Object.create(Parent.prototype)
// Child.prototype.constructor = Child
// var child1 = new Child('child1')
// console.log(child1) // Child{ sex: "boy", name: "child1" }
// child1.getName() // "child1"
// console.log(child1.__proto__) // Parent{}
// console.log(Object.create(null)) // {}
// console.log(new Object()) // {}
// 寄生组合继承算是ES6之前一种比较完美的继承方式吧。
// 它避免了组合继承中调用两次父类构造函数,初始化两次实例属性的缺点。
// 所以它拥有了上述所有继承方式的优点:
// 只调用了一次父类构造函数,只创建了一份父类属性
// 子类可以用到父类原型链上的属性和方法
// 能够正常的使用instanceOf和isPrototypeOf方法
/** --------------------------------------- */
// 6.原型继承
// function Create(obj) {
// function F() {}
// F.prototype = obj
// return new F()
// }
// var cat = {
// heart: '❤️',
// colors: ['white', 'black']
// }
// var guaiguai = Create(cat)
// var huaihuai = Create(cat)
// console.log(guaiguai)
// console.log(huaihuai)
// console.log(guaiguai.heart)
// console.log(huaihuai.colors)
// 该方法的原理是创建一个构造函数,构造函数的原型指向对象,然后调用 new 操作符创建实例,并返回这个实例,本质是一个浅拷贝。
// 在ES5之后可以直接使用Object.create()方法来实现
/** --------------------------------------- */
// 7.寄生式继承
// var cat = {
// heart: '❤️',
// colors: ['white', 'black']
// }
// function createAnother (original) {
// var clone = Object.create(original);
// clone.actingCute = function () {
// console.log('我是一只会卖萌的猫咪')
// }
// return clone;
// }
// var guaiguai = createAnother(cat)
// var huaihuai = Object.create(cat)
// guaiguai.actingCute()
// console.log(guaiguai.heart)
// console.log(huaihuai.colors)
// console.log(guaiguai)
// console.log(huaihuai)
// 实现方式:
// 在原型式继承的基础上再封装一层,来增强对象,之后将这个对象返回。
// 优点:
// 再不用创建构造函数的情况下,实现了原型链继承,代码量减少一部分。
// 缺点:
// 一些引用数据操作的时候会出问题,两个实例会公用继承实例的引用数据类
// 谨慎定义方法,以免定义方法也继承对象原型的方法重名
// 无法直接给父级构造函数使用参数
/** --------------------------------------- */
// 8. 混入方式继承多个对象
// function Parent() {
// this.name = 'parent'
// }
// Parent.prototype.getName = function() {
// console.log('parent')
// }
// function OtherParent(sex) {
// this.sex = sex
// }
// OtherParent.prototype.getColor = 'red'
// function Child (sex) {
// Parent.call(this)
// OtherParent.call(this, sex)
// }
// Child.prototype = Object.create(Parent.prototype)
// Object.assign(Child.prototype, OtherParent.prototype)
// Child.prototype.constructor = Child
// var child1 = new Child('boy')
// // console.log(child1)
// // child1.getName()
// // console.log(child1.getColor)
// console.log(Child.prototype.__proto__ === Parent.prototype) // true
// console.log(Child.prototype.__proto__ === OtherParent.prototype) // false
// console.log(child1 instanceof Parent) // true
// console.log(child1 instanceof OtherParent) // false
/** --------------------------------------- */
// 9. class中的继承
// class中的继承主要是extends和super
// class Parent {
// constructor (name) {
// this.name = name
// }
// getName() {
// console.log(this.name)
// }
// }
// class Child extends Parent {
// // constructor (name, sex) {
// // super(name)
// // this.sex = sex
// // }
// sex = 'boy' // 等同于注释部分
// }
// var child1 = new Child('child1', 'boy')
// console.log(child1)
// child1.getName()
// console.log(child1 instanceof Child)
// console.log(child1 instanceof Parent)
// extends 和 super必须一起使用
// 子类必须得在constructor中调用super方法,否则新建实例就会报错,
// 因为子类自己没有自己的this对象,而是继承父类的this对象,然后对其加工,如果不调用super的话子类就得不到this对象。
/** --------------------------------------- */
// class Parent {
// constructor () {
// console.log(new.target.name) // 此方法将调用两次
// }
// }
// class Child extends Parent {
// constructor () {
// const instance = super()
// console.log(instance)
// console.log(instance === this)
// }
// }
// const child1 = new Child()
// const parent1 = new Parent()
// console.log(child1, parent1)
// Child
// Child {}
// true
// Parent
// Child{} Parent{}
/** --------------------------------------- */
// class Parent {
// constructor (name) {
// this.name = name
// }
// }
// class Child extends Parent {
// constructor (name) {
// this.sex = 'boy' // this调用必须放到super之后
// super(name)
// }
// }
// var child1 = new Child('child1')
// console.log(child1)
// 子类constructor中如果要使用this的话就必须放到super()之后
// super当成函数调用时只能在子类的construtor中使用
/** --------------------------------------- */
// class Parent {
// constructor (name) {
// this.name = name
// }
// getName() {
// console.log(this.name)
// }
// }
// Parent.prototype.getSex = () => {
// console.log('boy')
// }
// Parent.getColors = () => {
// console.log(['red'])
// }
// class Child extends Parent {
// constructor (name) {
// super(name)
// super.getName()
// }
// initGetSex () {
// super.getSex()
// }
// static initColor () {
// super.getColors()
// }
// }
// const child1 = new Child('child1')
// child1.initGetSex()
// Child.initColor()
// console.log(child1)
/** --------------------------------------- */
// class Parent {
// constructor () {}
// }
// Parent.prototype.sex = 'boy'
// Parent.prototype.getSex = function () {
// console.log(this.sex)
// }
// class Child extends Parent {
// constructor () {
// super()
// this.sex = 'girl'
// super.getSex()
// }
// }
// var child1 = new Child()
// console.log(child1)
// ES6规定,通过super调用父类的方法时,super会绑定子类的this
/** --------------------------------------- */
// function Parent () {
// this.name = 'parent'
// }
// class Child1 extends Parent {}
// class Child2 {}
// class Child3 extends Array {}
// var child1 = new Child1()
// var child2 = new Child2()
// var child3 = new Child3()
// child3[0] = 1
// console.log(child1)
// console.log(child2)
// console.log(child3)
// extends后面接着的继承目标不一定要是个class。
// class B extends A {},只要A是一个有prototype属性的函数,就能被B继承。
// 由于函数都有prototype属性,因此A可以是任意函数。
/** --------------------------------------- */
// 总结:
/**
* ES6中的继承:
主要是依赖extends关键字来实现继承,且继承的效果类似于寄生组合继承
使用了extends实现继承不一定要constructor和super,因为没有的话会默认产生并调用它们
extends后面接着的目标不一定是class,只要是个有prototype属性的函数就可以了
super相关:
在实现继承时,如果子类中有constructor函数,必须得在constructor中调用一下super函数,因为它就是用来产生实例this的。
super有两种调用方式:当成函数调用和当成对象来调用。
super当成函数调用时,代表父类的构造函数,且返回的是子类的实例,也就是此时super内部的this指向子类。在子类的constructor中super()就相当于是Parent.constructor.call(this)。
super当成对象调用时,普通函数中super对象指向父类的原型对象,静态函数中指向父类。且通过super调用父类的方法时,super会绑定子类的this,就相当于是Parent.prototype.fn.call(this)。
ES5继承和ES6继承的区别:
在ES5中的继承(例如构造继承、寄生组合继承) ,实质上是先创造子类的实例对象this,然后再将父类的属性和方法添加到this上(使用的是Parent.call(this))。
而在ES6中却不是这样的,它实质是先创造父类的实例对象this(也就是使用super()),然后再用子类的构造函数去修改this。
*/