继承
prototype属性
- 当我们在类中定义方法的时候,这个方法在每次创建实例对象的时候,都会被调用,开辟新的内存空间,则会导致内存消耗很大,因此我们可以将这个类具有的方法放在原型对象上(prototype)
- 当我们修改原型上的属性的时候,就相当于在给该实例添加了一个这个属性
- 所有通过同一个构造函数创建的实例对象,都会共享同一个prototype,这个属性是个指针,指向一个对象,该对象的用途就是包含所有实例共享的属性和方法
function Person(newId,newName){
this.id=newId;
this.name=newName;
// this.eat=function(){
// console.log(eat);
// }
// this.sleep=function(){
// console.log(sleep)
// }
}
Person.prototype.eat=function(){
console.log("eat");
}
Person.prototype.doing="做饭";
let p=new Person(12,"小王");
// 当
p.doing="洗衣服";
let p1=new Person(13,"小明");
console.log(p,p1)
可以通过delete
来删除这个属性
delete p.doing;
官方原型属性添加方法
let arr=[1,3,4,5,6];
Array.prototype.max=function(){
let max=this[0];
for(let i=0;i<this.length;i++){
if(max<this[i]){
max=this[i];
}
}
return max;
}
console.log(arr.max())//6
区分原型的属性和实例的属性
实例:用new调用构造函数创建出来的对象叫做实例
原型属性:写在prototype后面的叫做原型属性
实例属性:创建出来的对象,重新给原型属性赋值后,就成为了实例属性
函数(类)的属性: prototype
实例的属性:proto
原型继承
- 子类自动拥有父类的属性和方法就称为继承,继承可以提高代码的复用性。并且子类可以添加新的属性(自身)的属性和方法。
- JS里的继承主要依靠的是原型链。让原型对象(每一个构造函数都有一个原型对象)的值,等于另一个类型的实例,即实现了继承。
- 另外一个类型的原型再指向第三个类型的实例,以此类推,也就形成了一个原型链
function Animal(newName,newAge){
this.name=newName;
this.age=newAge;
}
Animal.prototype.eat=function(str){
console.log(this.name+"爱吃"+str);
}
function Person(newId){
this.id=newId;
}
Person.prototype=new Animal("老王",18);
Person.prototype.listen=function(str){
console.log(this.name+"听"+str);
}
let p=new Person("999");//实例中有一个_proto_属性指向原型对象
console.log(p.name,p.age,p.id);
p.eat("米饭");
let a=new Animal();
// 使用instanceof操作符检测对象类型
console.log(p instanceof Person);//true
console.log(a instanceof Animal);//true
原型继承中的注意事项:
- 先定义原型继承关系,再添加子类的自定义方法或属性(原型的属性,即共享的属性和方法要放在原型继承关系确立后,再定义,类对象在添加新的方式时,一定要保证,先实现继承,在添加原型方法)
- 利用原型链继承,给子类添加原型方法的时,不可以重写prototype(:由父类派生给子类的属性,无法初始化)
缺点 - 被继承的类型(父类)里包括引用类型的属性的时候,它会被所有实例共享其值
- 创建子类型的实例时,没法传参给被继承类型
call和apply的继承
bind、call、apply的区别
bind改变匿名函数的this指向
setTimeout(function(){
console.log(this);//document
}.bind(document),1000)
apply()、call()
apply和call改变有名函数的this指向
第一个参数为this的指向
第二个参数为函数的实参
apply 传入函数的实参时候,传入的是一个数组
call 传入函数的实参的时候,实参分开写
function fun(a,b){
console.log(this)
console.log(a+b);
}
fun.apply(document,[1,2])//参数为数组
fun.call(document,1,2)//参数分开写
当一个方法被多个类进行调用的时候,如果在原型上定义该方法,内存还是会浪费,因此我们可以定义一个接口,此时则需要借助apply和call的方法改变this指向,从而实现不同的对象实现不同的消息响应
function Person(newId,newName){
this.id=newId;
this.name=newName;
}
// Person.prototype.eat=function(){
// console.log(this.name+"吃"+str+str2)
// }
function Student(newId,newName){
this.id=newId;
this.name=newName;
}
// Student.prototype.eat=function(str,str2){
// console.log(this.name+"吃"+str+str2)
// }
function eat(str,str2){
console.log(this.name+"爱吃"+str+str2)
}
let p=new Person("1","小王")
let s=new Student("2","小明")
// 接口:发出一个消息,不同对象实现不同的消息响应
eat.call(p,"面包","米饭");
eat.apply(s,["米饭",""])
call和apply模拟继承
可以解决原型继承没有办法进行初始化的问题
原型继承缺陷:父类的属性派生给子类时,子类对象无法初始化父类继承来的属性,
call和apply继承可以进行进行初始化
function Person(newId,newName){
this.id=newId;
this.name=newName;
}
Person.prototype.eat=function(){
console.log("Person eat");
}
function Student(newId,newName,newScore){
// 借用构造方法
Person.call(this,newId,newName);
this.score=newScore;
}
let s=new Student(1,"老王",100);
console.log(s.id,s.name,s.score)
当子类访问父类原型上的方法的时候,出现了报错,因此子类不能继承父类原型上的方法
s.eat();
混合继承
属性用apply和call
方法用原型
解决了原型继承和call,apply继承的缺陷
- 父类的属性派生给子类时,子类对象可以初始化父类继承来的属性
- 同时子类也会继承父类原型上的方法
function Person(newId,newName){
this.id=newId;
this.name=newName;
}
Person.prototype.eat=function(){
console.log("Person eat");
}
function Student(newId,newName,newScore){
// 借用构造方法
Person.call(this,newId,newName);
this.score=newScore;
}
Student.prototype=new Person();
Student.prototype.study=function(){
console.log("Student study")
}
let s=new Student(1,"老王",100);
s.eat();
s.study()
console.log(s.id,s.name,s.score)
ES6继承
extends
:继承的关键字
super
相当于父类的constructor
super
一定要放在构造函数的第一位
class Person {
constructor(newId,newName) {
this.id=newId;
this.name=newName;
}
eat(){
console.log('Person eat');
}
}
class Student extends Person{
constructor(newId,newName,newScore) {
//借用父类的构造方法
super(newId,newName);
this.score=newScore;
}
study(){
console.log("Student study")
}
}
let s=new Student(1,"老王",100);
s.eat();
s.study()
console.log(s.id,s.name,s.score)