原型链
实例与Object原型对象之间的链条称为原型链
原型模式的访问机制(原型搜索机制)
- 读取实例对象的属性时,先从实例对象本身开始搜索。如果在实例中找到了这个属性,则返回该属性的值;
- 如果没有找到,则继续搜索实例的原型对象,如果在原型对象中找到了这个属性,则返回该属性的值
- 如果还是没找到,则向原型对象的原型对象查找,依此类推,直到Object的原型对象(最顶层对象);
- 如果再Object的原型对象中还搜索不到,则抛出错误;
演示:画图讲解原型链继承
继承
继承是面向对象中一个非常重要的特征。指的是:子类继承父类的属性和方法。
我们可以通过继承的方式, 在父类的属性和方法基础上, 让子类也拥有这些属性和方法, 并可以扩展。
继承的好处:
- 子类拥有父类所有的属性和方法(代码复用);
- 子类可以扩展自己的属性和方法(更灵活);
- 子类可以重写父类的方法
继承方式
原型链继承
- 核心:拿父类实例来充当子类原型对象
- 缺点:
- 无法操作父类构造函数的属性
1 | function Person(name,age,gender){ |
借用构造函数
核心:借父类的构造函数来增强子类实例,相当于把父类的实例属性复制一份给子类实例
- call:
- 格式:父类构造函数.call(子类实例,参数1,参数2,参数3…)
apply:
格式:父类构造函数.apply(子类实例,[参数1,参数2,参数3…])
call与apply的唯一区别:传参方式不同,call多个参数,apply只有两个参数,第二个参数为数组1
2
3//aplly用法:借用方法
var arr = [20,2,40,33,21,8,22,46,32]
Math.max.apply(null,arr)
组合继承
由于以上继承方法的缺点,实际开发中不可能单纯的只使用一种继承方法,而是利用它们的优点,规避它们的缺点,所以就有了组合继承法
- 继承属性:借用构造函数
- 只在构造函数中定义属性
- 继承方法:原型链继承
- 把所有的方法写入原型对象
组合继承是最常用的继承模式。
- 缺点(原型链继承法的缺点):
- 在原型对象中生成多余的属性
- 多次执行父类构造函数
1 | //原型链继承基础上补充借用构造函数 |
原型式继承
核心:先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例
解决原型链继承法的缺点:生成多余的属性
1
2
3
4
5
6function object(o){
function F(){}
F.prototype = o;
return new F();
}
Man.prototype = object(Person.prototype);
ES5版本的原型式继承:Object.create()
寄生组合继承法
完美的继承方法
- 核心:
- 继承属性:借用构造函数
- 继承方法:原型式继承
ES6继承
Class定义类
- 写在类里面的方法实际是给Person.prototype添加方法
- constructor方法是类的默认方法,相当于在构造函数内生成属性
extends继承
- 子类继承了父类,在子类构造函数中必须调用super方法。
- 子类的constructor方法没有调用super之前,不能使用this关键字,否则报错,而放在super方法之后就是正确的。
静态方法
如果在一个方法前,加上static关键字,这就称为“静态方法”
静态方法方法不会被实例继承,而是直接通过类来调用
Person.getInfo()
父类的静态方法,可以被子类继承
Man.getInfo()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class Person{
constructor(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
// 此处的方法写入原型对象中
say(){console.log(大家好,我叫${this.name})};
static eat(){console.log('我是吃货');}
}
let lemon = new Person('lemon',32,'女');
// 继承方法
class Man extends Person{
constructor(name,age){
// 继承属性
super(name,age,'男');
this.hobby = ['吃','喝'];
}
}
let laoxie = new Man('laoxie',18);
call和apply的使用
1 | * 求数组中最大数和最小数 |