JS中的继承也是面试中常问的一块,常常是知其然而不知其所以然,看了视频后做一些笔记加深一下印象,以后好复习
继承
子类继承父类中的属性和方法:目的是让子类的实例能够调取父类的属性和方法
面向对象
-
封装:低耦合高内聚
-
多态:重载(方法名相同,形参个数或者类型不一样)与重写(在类的继承中,子类可以重写父类中的方法)
public void sum(int n,int m){ } public void sum(int n,int m,float x){ } public void sum(int n,String m){ } //后台通过重载减轻一个方法业务逻辑复杂度,减少并发
JS中不存在真正意义上的重载,JS中的重载指的是同一个方法,根据传参(arguments)不同,实现出不同的效果
面试问题:什么是面向对象?谈谈对它的理解
- 面向对象是一种编程思想,JS本身就是基于面向对象构建出来的(例如JS中有很多内置类,像Promise就是ES6中新增的一个内置类,我们可以基于new Promise来创建一个实例,来管理异步编程,我在项目中,Pormise也经常用,自己也研究过它的源码…),我之前看过一点框架源码,我们平时用的vue/react/jquery也是基于面向对象构建出来的,他们都是类,平时开发的时候都是创建他们的实例来操作的,当然自己在真实项目中也封装过一些组件插件(例如dialog、拖拽…),也是基于面向对象开发的,这样可以创造不同的实例,来管理私有的属性和公有的方法,很方便…
- JS中的面向对象,和其他编程语言还是有略微不同的,JS中类和实例是基于原型和原型链机制来处理的,而且JS中关于类的重载、重写、继承也和其他语言不太一样…
自己引申的问题:组件化与模块化
- 组件化:把重复的代码提取出来合并成为一个个组件,组件最重要的就是重用(复用)-- 组件化开发就应用了面向对象的思想
- 模块化:分属同一功能/业务的代码进行隔离(分装)成独立的模块,可以独立运行,独立管理,每个模块有很多接口,可供调用
原型继承
A代表父类,B代表子类
原型继承:让父类中的属性和方法在子类实例的原型链上
Child.prototype = new Parent()
Child.peototype.constructor = Child
function A(x){
this.x = x
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(y){
this.y = y
}
B.prototype = new A(200) //想要b1获得getX方法
B.prototype.constructor = B //保证原型重定向后的完整性
B.prototype.getY = function(){
console.log(this.y)
}
let b1 = new B(100)//继承之前b1有y属性以及getY方法,继承之后还有x属性以及getY方法
特点:
-
不像其他语言中的继承一样(其他语言的继承一般是拷贝继承,也就是子类继承父类,会把父类中的属性和方法拷贝一份到子类中,供子类的实例调取使用),它是把父类的原型放到子类实例的原型链上,实例想要调取这些方法,是基于__proto__原型链查找机制完成
-
子类可以重写父类上的方法(导致父类其他的实例也受到影响)
//举例 B.prototype.__proto__.sum = function(){}
-
父类中私有或者公有的属性方法,最后都会变为子类中公有的属性和方法
call继承
Child方法中把Parent当作普通函数执行,让Parent中的this指向Child的实例,相当于给Child的实例设置了更多的私有的属性或者方法
function A(x){
this.x = x
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(y){
//==> this:B的实例b1
A.call(this,200)
this.y = y
}
B.prototype.getY = function(){
console.log(this.y)
}
let b1 = new B(100)
特点:
- 只能继承父类私有的属性或者方法(因为是把Parent当作普通函数执行,和其原型上的属性和方法没有关系)
- 父类私有的变为子类私有
寄生组合继承
call继承+原生式继承
function A(x){
this.x = x
}
A.prototype.getX = function(){
console.log(this.x)
}
function B(y){
//==> this:B的实例b1
A.call(this,200)
this.y = y
}
//Object.create(obj):创建一个空对象,让空对象__proto__指向obj
B.prototype = Object.create(A.prototype)
B.prototype.constructor = B
B.prototype.getY = function(){
console.log(this.y)
}
let b1 = new B(100)
//Object.create的实现
Object.create = function(obj){
function Fn(){}
Fn.prototype = obj
return new Fn()
}
ES6中的继承
ES6中基于class创造出来的类不能当作普通函数执行,所以call继承失效,也不允许重定向原型的指向
class Child extends Parent{//==> B.prototype.__proto__ = A.prototype
constructor(){
super() //==> A.call(this),让方法中的this是子类的实例
}
}
class A{
constructor(x){
this.x = x //私有属性
}
getX(){
console.log(this.x) //原型链上的方法
}
// A.prototype.x = xx //在原型链上添加属性
}
class B extends A{
constructor(y){
super(200)//子类继承父类,可以不写constructor(浏览器会自己默认创建=> constructor(...args){super(...args)}),一旦写了,则在constructor中的第一句话必须是super()
this.y = y //私有属性
}
getY(){
console.log(this.y) //原型链上的方法
}
}
let b1 = new B(100)
应用
class Utils{
query(){}
}
class Dialog extends utils{
constructor(){
super()
this.query()
}
}
new B(100)
# 应用
```js
class Utils{
query(){}
}
class Dialog extends utils{
constructor(){
super()
this.query()
}
}