类的继承和多态
封装:
- 把实现一个功能的代码放在一个函数中封装起来,以后再想实现这个功能的时候,我们不需要重新的编写代码了,只需要执行对应的函数即可,我们把这种机制就称之为”函数的封装”–>低耦合高内聚:减少页面中的冗余代码,提高代码的重复使用率
1
2
3
4
5function fn(){
实现功能的代码
}
fn();
fn();
多态:
后台语言[java、C#、ASP.net、php…]中的多态:重载、重写
重载(Reload):多个方法,方法名相同,参数的个数或者参数的类型不一样
重写(Rewrite):子类重写父类的方法
1)JS中如果函数名相同了,后面的会把前面的函数给覆盖掉,所以JS中不存在重载
fn(); ->2
functionfn(){console.log(1);}
2)JS中的多态指的是一个方法的多种形态:一个方法,我们通过传递进来的参数值的不一样,实现不一样的功能
根据传递的n和m的值不一样,我们获取的结果也不一样,例如:如果执行的时候不传递任何的参数,返回[0-1)之间的随机小数,如果传递了两个值1/10,返回的是[1-10]之间的随机整数1
2
3
4
5
6
7
8
9
10
11
12
13function getRandom(n,m){
n=Number(n);
m=Number(m);
if(isNaN(n) || isNaN(m)){
returnMath.random();
}
if(n>m){
n=m+n;
m=n-m;
n=n-m;
}
return Math.round(Math.random()*(m-n)+n);
}
继承:
JS中常用的继承有以下几种方式(子类想要继承父类中的属性和方法)
1)原型链继承
(我们项目中最常用的一种继承方式)
A、子类的原型指向父类的一个实例即可->原理不是把父类中的方法直接的继承复制过来一份,而是通过原型链的查找机制一级级的向上查找使用的
B、原型继承是把父类中私有和公有的属性和方法都当做子类中公有的
C、子类可以通过原型链的查找机制把父类原型上的属性和方法进行添加、修改、删除,如果这样操作完成后,我们父类的其他实例都会受到影响->子类重父类的方法->类的重写,例如:c.proto.proto.getX=function(){} 相当于子类重写了父类原型上的公有的方法1
2
3
4
5function Parent(){this.x = 100;}
Parent.prototype.getX =function () {console.log(this.x);};
function Children(){this.x = 200;}
Children.prototype = newParent;
var c = newChildren;
####2)call继承(借用构造函数继承)
A、在子类的函数体中,把父类当做一个普通的函数执行,让父类执行的时候里面的this变为子类的一个实例,这样就相当于直接的给子类的实例增加私有的属性和方法
B、call继承只能继承父类私有的属性和方法,而且继承过来后也是子类实例自己私有的
C、call继承是直接的把父类中的私有属性和方法相当于克隆一份直接的给子类1
2
3
4
5
6
7
8
9function Parent(){this.x = 100;this.writeX = function () {}}
Parent.prototype.getX =function () {console.log(this.x);};
function Children(){
//this->Children的实例c
Parent.call(this);//把Parent当做普通的函数执行,让里面的this变为c->在执行的时候相当于c.x=100、 c.writeX=function...
}
var c = newChildren;
var d = newChildren;
console.log(c.writeX ===d.writeX);//-->false
3)冒充对象继承
A、在子类的函数体中,我们创建一个父类的实例,然后把这个实例当做一个普通的对象进行遍历,在每一次遍历的时候,把父类中私有/公有的属性和方法,都当做子类实例的私有属性和方法进行存储(使用 for in 循环可以遍历出类私有属性和自定义的属性方法)
B、父类的私有和公有都可以继承,而是是相当于复制了一份继承过来的
C、继承过来的都是子类实例私有的属性和方法1
2
3
4
5
6
7
8
9
10
11
12
13function Parent(){this.x = 100;this.writeX = function () {}}
Parent.prototype.getX =function () {console.log(this.x);};
function Children(){
var temp=new Parent();
//temp就是Parent的一个实例:x、writeX、getX
for(var key in temp){
this[key]=temp[key];
}
temp=null;
}
var c = newChildren;
var d = newChildren;
console.log(c.getX ===d.getX);
4)组合式继承(call继承和原型链继承)
- 原型继承和call继承/冒充对象继承,任意一个组合到一起来实现的,一般来说都是和call继承组合
- 缺点:
- 组合式继承 1.子类会把父类的实例属性(私有属性),继承过来两份
- 一份存储在子类实例对象本身,另一份存储在原型链上
- b insaceof A =>TRUE 只要能在原型链上查找到,那么返回的就是ture
- 调用了两次父类,产生两个作用域,消耗浏览器内存
1
2
3
4
5
6
7
8
9
10
11
12function Parent() {
this.x = 100;
this.writeX = function () {}
}
Parent.prototype.getX =function () {
console.log(this.x);
};
function Children(){
Parent.call(this);
}
Children.prototype = newParent;
var c = newChildren;
5)原型式继承
- 只能继承父类的公有方法,通过Object.create()方法
- create : 首先创建一个新对象,并且把第一个参数 作为 这个新对象的原型
- IE低版本浏览器不支持这种ES5方法
1
2
3
4function A (){}
B.prototype = Object.create(A.prototype)//=> B.prototype.__proto__ === A.prototype
B.prototype = Object.create(null) //=> {}
function B (){}
6)寄生组合式继承(最理想的继承方法)
1 | function A (){ |
7)ES6中的继承
1.ES6中创建类是由自己标准语法的,这种语法创建出来的类,只能new执行,不能当做普通函数执行
1
2
3
4
5
6
7
8
9
10
11class Fn { //=>Fn是类名,没有小括号
constructor (n,m) {
//=>等价于传统ES5类的构造体
this.x = n
this.y = m
}
getX () {...}//=>给Fn的原型上设置方法(只能设置方法,不能设置属性)
static AA(){...}//=>把Fn当做一个普通对象,添加私有属性(和实例没有关系,同样也只能设置方法)
}
let f = new Fn (10,20)
console.log(f.x,f.y)//=> (10,20)2.ES6中类的继承 这种方法保留了子类B的constructor属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class A {
constructor(n){
console.log(n)//=>100
this.x = 100
}console.log(n)//=>100console.log(n)//
getX(){console.log(this.x)}
}
class B extends A{//=>extends类似于实现了原型继承
constructor(){
super(100)//=>类似于call继承,在这里super相当于把A的constructor给执行了,并且让方法中的this是B的实例
this.y = 200
}
getY(){console.log(this.y)}
}
let f = new B()