一、预备知识
1.构造函数的属性
function A(name){
this.name = name; //实例基本属性(该属性强调私有 不共享)
this.arr = [1]; //实例引用属性(该属性强调私有 不共享)
this.say = function() { //实例引用属性 (该属性强调复用 需要共享)
console.log('helloworld');
}
}
.什么是原型对象
简单来说,每个函数都有prototype属性,它就是原型对象,通过函数实例化出来的对象有个__proto__属性,指向原型对象。
let a = new A()
a.__proto__ == A.prototype
//prototype的结构如下
A.prototype = {
constructor: A,
...其他的原型属性和方法
}
继承是什么?
面向对象的三大特征:封装、继承、多态。作用就是判断代码是否是面向对象的思维方式。面向对象的思维方式:注重结果。
继承就是指一个子类继承父类的属性和方法,使得子类对象(实例化对象)具有父类的方法和属性。
这里要明白JavaScript是通过原型链实现继承的。
继承常见的六种方式:原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承。
JavaScript的继承是一种实现对象之间共享属性和方法的方式,它使用原型链来实现。
在JavaScript中,每个对象都有一个原型(prototype),原型是一个对象,它包含了要继承的属性和方法。当我们访问对象的属性或方法时,如果对象本身没有,则会去它的原型上查找,如果原型上还没有,则会继续向上层的原型链查找,直到找到或者到达原型链的顶层。
通过原型链,子对象可以继承父对象的属性和方法,这样可以实现代码的复用和共享。
需要注意的是,在实现继承时,可以使用构造函数、原型链组合使用的方式,或者使用ES6中的类和继承关键字等方式。这些方式都可以用来创建具有继承关系的对象。
原型链继承
这种方式关键在于:子类型的原型为父类型的一个实例对象。
外注意一点的是,我们需要在子类中添加新的方法或者是重写父类的方法时候,切记一定要放到替换原型的语句之后
特点:
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 无法实现多继承
- 来自原型对象的所有属性被所有实例共享
- 创建子类实例时,无法向父类构造函数传参
- 要想为子类新增属性和方法,必须要在
Student.prototype = new Person()之后执行,不能放到构造器中
借用构造函数继承
借用构造函数继承(Constructor Borrowing Inheritance)是一种实现对象之间继承关系的方法。在这种方式中,子对象通过调用父对象的构造函数,并使用 call 或 apply 方法将父对象的属性和方法应用到子对象上。
通过借用构造函数继承,子对象可以继承父对象的属性,并且每个子对象都会拥有父对象属性的独立副本。这种继承方式可以避免子对象之间共享属性时可能出现的问题。
// 借用构造函数 继承 属性的继承
function Persion(name, age) {
this.type = '搬砖'
this.name = '昌平小白龙'
this.age = 18
}
Persion.prototype.sayName = function () {
console.log(123)
}
// bind apply call
function Student(name, age) {
Persion.call(this, name, age)
this.job = '赚钱'
}
let s1 = new Student('大聪明', 20)
console.log(s1.name, s1.age, s1.type, s1.job)
// console.log(s1.sayName())
优点:
- 解决了子类构造函数向父类构造函数中传递参数
- 可以实现多继承(call或者apply多个父类)
缺点:
- 方法都在构造函数中定义,无法复用
- 不能继承原型属性/方法,只能继承父类的实例属性和方法
实例继承(原型式继承)
实例继承,又被称为原型式继承(Prototype-based inheritance),是一种通过直接使用已有对象作为新对象的原型来实现继承的方法。
在 JavaScript 中,每个对象都有一个特殊的属性称为原型(prototype),它指向另一个对象。在创建一个新对象时,可以通过指定原型来实现继承。新对象将从原型对象继承属性和方法。
优点:
- 解决了子类构造函数向父类构造函数中传递参数
- 可以实现多继承(call或者apply多个父类)
缺点:
- 方法都在构造函数中定义,无法复用
- 不能继承原型属性/方法,只能继承父类的实例属性和方法
组合式继承 (组合原型链继承和借用构造函数继承)【常用】 组合继承(combination inheritence)
有时候也叫做伪经典继承
指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式
思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性
原文链接:blog.csdn.net/qq_42496307…
优点:
- 函数可以复用
- 每个新实例引入的构造函数属性是私有的。
- 可以继承父类原型上的属性,可以传参,可复用。
缺点:
- 调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
寄生组合继承【常用】
-
实质:寄生在原型式继承上的继承
-
实现:在原型式继承的基础上创建了一个原实例对象之后,为新创建的原型式实例对象加入一个属性和方法,然后再返回这个实例对象,这个就是寄生在原型式继承上的寄生式继承
寄生:
-
在函数内返回对象然后调用
组合: -
函数的原型等于另一个实例。
-
在函数中用apply或者call引入另一个构造函数,可传参
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class People{
constructor(name='wang',age='27'){
this.name = name;
this.age = age;
}
eat(){
console.log(`${this.name} ${this.age} eat food`)
}
}
//继承父类
class Woman extends People{
constructor(name = 'ren',age = '27'){
//继承父类属性
super(name, age);
}
eat(){
//继承父类方法
super.eat()
}
}
let wonmanObj=new Woman('xiaoxiami');
wonmanObj.eat();
ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。
优点:语法简单易懂,操作更方便
缺点:并不是所有的浏览器都支持class关键字
继承图解
** ES5继承和ES6继承的区别:
es5继承首先是在子类中创建自己的this指向,最后将方法添加到this中 Child.prototype=new Parent() || Parent.apply(this) || Parent.call(this) es6继承是使用关键字先创建父类的实例对象this,最后在子类class中修改this 最后总结:
继承这些知识点与其说是对象的继承,更像是函数的功能用法,如何用函数做到复用,组合,这些和使用继承的思考是一样的。上述几个继承的方法都可以手动修复他们的缺点,但就是多了这个手动修复就变成了另一种继承模式。 这些继承模式的学习重点是学它们的思想,不然你会在coding书本上的例子的时候,会觉得明明可以直接继承为什么还要搞这么麻烦。就像原型式继承它用函数复制了内部对象的一个副本,这样不仅可以继承内部对象的属性,还能把函数(对象,来源内部对象的返回)随意调用,给它们添加属性,改个参数就可以改变原型对象,而这些新增的属性也不会相互影响。 **
好了今天的知识就分享到这里了 望大家多多支持点点赞 小编不易
参考文章
-
JavaScript常见的继承方式
-
阮一峰ES6入门之class的继承
求关注 交流分享