方式一、原型链继承

这种方式关键在于:子类型的原型为父类型的一个实例对象。

--------------------------------------------------------------------
注:如果你对python感兴趣,我这有个学习Python基地,里面有很多学习资料,感兴趣的+Q群:895817687
--------------------------------------------------------------------//父类型function Person(name, age) {this.name = name,this.age = age,this.play = [1, 2, 3]this.setName = function () {}}Person.prototype.setAge = function () {}//子类型function Student(price) {this.price = pricethis.setScore = function () {}}Student.prototype = new Person() // 子类型的原型为父类型的一个实例对象var s1 = new Student(15000)var s2 = new Student(14000)console.log(s1,s2)


但这种方式实现的本质是通过将子类的原型指向了父类的实例,所以子类的实例就可以通过proto__访问到 Student.prototype 也就是Person的实例,这样就可以访问到父类的私有方法,然后再通过__proto指向父类的prototype就可以获得到父类原型上的方法。于是做到了将父类的私有、公有方法和属性都当做子类的公有属性
子类继承父类的属性和方法是将父类的私有属性和公有方法都作为自己的公有属性和方法,我们都知道在操作基本数据类型的时候操作的是值,在操作引用数据类型的时候操作的是地址,如果说父类的私有属性中有引用类型的属性,那它被子类继承的时候会作为公有属性,这样子类1操作这个属性的时候,就会影响到子类2。

s1.play.push(4)
console.log(s1.play, s2.play)
console.log(s1.__proto__ === s2.__proto__)//true
console.log(s1.__proto__.__proto__ === s2.__proto__.__proto__)//true
```![在这里插入图片描述](https://img-blog.csdnimg.cn/20190712140341379.png)
s1中play属性发生变化,与此同时,s2中play属性也会跟着变化。
另外注意一点的是,我们需要在子类中添加新的方法或者是重写父类的方法时候,切记一定要放到替换原型的语句之后```java
function Person (name, age) {this.name = name,this.age = age
}
Person.prototype.setAge = function () {console.log("111")
}
function Student (price) {this.price = pricethis.setScore = function () { }
}
// Student.prototype.sayHello = function () { }//在这里写子类的原型方法和属性是无效的,
//因为会改变原型的指向,所以应该放到重新指定之后
Student.prototype = new Person()
Student.prototype.sayHello = function () { }
var s1 = new Student(15000)
console.log(s1)

特点:

父类新增原型方法/原型属性,子类都能访问到
简单,易于实现

缺点:

无法实现多继承
来自原型对象的所有属性被所有实例共享
创建子类实例时,无法向父类构造函数传参
要想为子类新增属性和方法,必须要在Student.prototype = new Person() 之后执行,不能放到构造器中

方式二: 借用构造函数继承

这种方式关键在于:在子类型构造函数中通用call()调用父类型构造函数

function Person(name, age) {this.name = name,this.age = age,this.setName = function () {}}Person.prototype.setAge = function () {}function Student(name, age, price) {Person.call(this, name, age)  // 相当于: this.Person(name, age)/*this.name = namethis.age = age*/this.price = price}var s1 = new Student('Tom', 20, 15000)

这种方式只是实现部分的继承,如果父类的原型还有方法和属性,子类是拿不到这些方法和属性的。

console.log(s1.setAge())//Uncaught TypeError: s1.setAge is not a function

特点:

解决了原型链继承中子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)

缺点:

实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性和方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

方式三: 原型链+借用构造函数的组合继承

这种方式关键在于:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。

function Person (name, age) {this.name = name,this.age = age,this.setAge = function () { }
}
Person.prototype.setAge = function () {console.log("111")
}
function Student (name, age, price) {Person.call(this, name, age)this.price = pricethis.setScore = function () { }
}
Student.prototype = new Person()
Student.prototype.constructor = Student//组合继承也是需要修复构造函数指向的
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
var s2 = new Student('Jack', 22, 14000)
console.log(s1)
console.log(s1.constructor) //Student
console.log(p1.constructor) //Person


这种方式融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。不过也存在缺点就是无论在什么情况下,都会调用两次构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数的内部,子类型最终会包含父类型对象的全部实例属性,但我们不得不在调用子类构造函数时重写这些属性。

优点:

可以继承实例属性/方法,也可以继承原型属性/方法
不存在引用属性共享问题
可传参
函数可复用

缺点:
调用了两次父类构造函数,生成了两份实例

方式四: 组合继承优化1

这种方式通过父类原型和子类原型指向同一对象,子类可以继承到父类的公有方法当做自己的公有方法,而且不会初始化两次实例方法/属性,避免的组合继承的缺点。

function Person (name, age) {this.name = name,this.age = age,this.setAge = function () { }
}
Person.prototype.setAge = function () {console.log("111")
}
function Student (name, age, price) {Person.call(this, name, age)this.price = pricethis.setScore = function () { }
}
Student.prototype = Person.prototype
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
console.log(s1)


但这种方式没办法辨别是对象是子类还是父类实例化

console.log(s1 instanceof Student, s1 instanceof Person)//true true
console.log(s1.constructor)//Person

**优点:**不会初始化两次实例方法/属性,避免的组合继承的缺点

**缺点:**没办法辨别是实例是子类还是父类创造的,子类和父类的构造函数指向是同一个。

方式五: 组合继承优化2

借助原型可以基于已有的对象来创建对象,var B = Object.create(A)以A对象为原型,生成了B对象。B继承了A的所有属性和方法。

function Person (name, age) {this.name = name,this.age = age
}
Person.prototype.setAge = function () {console.log("111")
}
function Student (name, age, price) {Person.call(this, name, age)this.price = pricethis.setScore = function () { }
}
Student.prototype = Object.create(Person.prototype)//核心代码
Student.prototype.constructor = Student//核心代码
var s1 = new Student('Tom', 20, 15000)
console.log(s1 instanceof Student, s1 instanceof Person) // true true
console.log(s1.constructor) //Student
console.log(s1)

同样的,Student继承了所有的Person原型对象的属性和方法。目前来说,最完美的继承方法!

方式六:ES6中class 的继承

ES6中引入了class关键字,class可以通过extends关键字实现继承,还可以通过static关键字定义类的静态方法,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。

class Person {//调用类的构造方法constructor(name, age) {this.name = namethis.age = age}//定义一般的方法showName () {console.log("调用父类的方法")console.log(this.name, this.age);}
}
let p1 = new Person('kobe', 39)
console.log(p1)
//定义一个子类
class Student extends Person {constructor(name, age, salary) {super(name, age)//通过super调用父类的构造方法this.salary = salary}showName () {//在子类自身定义方法console.log("调用子类的方法")console.log(this.name, this.age, this.salary);}
}
let s1 = new Student('wade', 38, 1000000000)
console.log(s1)
s1.showName()


优点:语法简单易懂,操作更方便
缺点:并不是所有的浏览器都支持class关键字

JavaScript 常见的六种继承方式相关推荐

  1. JavaScript六种继承方式的递进推演

    1. 原型链继承 function Parent1() {this.name = "Parent1"this.son = [1] } // 需要继承的子类 function Chi ...

  2. javascript 的七种继承方式(三)组合继承

    组合继承 前面两篇我们了解到:原型链继承存在着引用类型问题,而借用构造函数又无法实现函数复用和原型方法继承的问题.那么能不能把两者结合一下,取其精华,弃其糟粕?答案是肯定的.那就是接下来我们要介绍的组 ...

  3. JavaScript中6种继承方式总结

  4. 前端进击的巨人(七):走进面向对象,原型与原型链,继承方式

    "面向对象" 是以 "对象" 为中心的编程思想,它的思维方式是构造. "面向对象" 编程的三大特点:"封装.继承.多态" ...

  5. JavaScript中6种常见的继承方式

    为什么需要继承? 在实际编码的过程中,如果有很多类似的方法都存放于构造函数中,这样会导致内存的浪费(内存泄漏),在这种情况下我们就需要用到继承. 继承是什么? 所谓继承就是通过某种方式让一个对象可以访 ...

  6. Js理解之路:Js常见的6中继承方式

    目录 一.JS 实现继承的几种方式 第一种:原型链继承 二.构造函数继承(借助call方法) 三.组合继承(原型链继承+构造函数继承) 第四种:原型式继承(借助Object.create) 第五种:寄 ...

  7. 探究JS常见的6种继承方式

    先看以下百科对(面向对象的继承)的解释! 通过以上精炼实用的解释,我们可以了解到继承的基本作用和功能!即可以使得子类具有父类的属性和方法或者重新定义.追加属性和方法等. 广告:帮忙点击>> ...

  8. js常见的的6种继承方式

    继承是面向对象的,继承可以帮助我们更好的复用以前的代码,缩短开发周期,提高开发效率:继承也常用在前端工程技术库的底层搭建上,在整个js的学习中尤为重要 常见的继承方式有以下的六种 一.原型链继承 原型 ...

  9. jQuery-源码阅读,JavaScript原生继承方式与jQuery中的继承

    JavaScript中继承方法有以下几种: 1.原型链继承: function Book (name,author){this.name=name;this.author=author;}Book.p ...

最新文章

  1. 电子合同的履行_什么是电子合同履行?怎么履行电子合同?
  2. 【直播】杨剑砺:数据可视化漫谈
  3. 阿里感悟(九)-如何才能晋升
  4. RS-232 Receiver
  5. 【Electron】Error: A dynamic link library (DLL) initialization routine failed.
  6. 阿里云ACM:云原生配置管理利器,让云上的Spring Cloud应用配置管理舞动起来
  7. 1688.比赛中的配对次数-LeetCode
  8. Q96:PT(3):基于噪声的纹理(Noise-Based Textures)(0)——概述
  9. 华为云计算HCNA--存储虚拟化
  10. IEEE 会议模板介绍
  11. Proteus总线连接心得
  12. 第一个vue-cli程序
  13. 这次把怎么做好一个PPT讲清-演讲篇
  14. android 关机 流程_android 关机 流程分析
  15. oracle计算timestamp差,Oracle 计算timestamp和date类型的时间差
  16. Mysql数据库表结构设计导出
  17. 丁奇的MySQL实战45讲 学习笔记[链接]
  18. 高德地图中隐藏Marker的标记
  19. IDEA的Maven Show Dependency使用心得
  20. 阿里云服务器购买指南

热门文章

  1. 04 | 内存快照:宕机后, Redis 如何实现快速恢复
  2. mysql执行计划_mysql的sql执行计划详解
  3. python异常处理机制的好处_浅谈Python异常处理机制
  4. python画海绵宝宝_脑洞大开的万圣节,10招带你和宝宝花式玩南瓜
  5. 实验1 最小生成树问题【Kruskal+Prim】
  6. idea 编译显示source1.3不支持泛型(请使用source5或更高版本)
  7. 解决在非spring上下文的环境中无法获取Spring容器的bean【nullpointer:connot invoke because xxx is null问题】
  8. Spring-Kafka消费者源码阅读笔记
  9. JavaScript基础08-day10【函数的返回值、实参、立即执行函数、方法、全局作用域、函数作用域、提前声明、this、工厂方法创建对象、构造函数】
  10. linux下利用valgrind工具进行内存泄露检测和性能分析