💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 面向对象复习 ## 普通构造函数 ~~~ function Person(name, age) {  this.name = name;  this.age = age; } Person.prototype.say = function(){  console.log('i can say') } Person.go = function () {  console.log('走一下'); } ~~~ ~~~ var p1 = new Person('小白',10); console.log(p1); p1.say(); Person.go(); ~~~ ## class关键词 ~~~ class Per{  constructor(name,age){    this.name  = name;    this.age = age; }  say(){    console.log('i can say') }  static go(){    console.log('要走一走') } } var p2  = new Per('小黑',15); console.log(p2); p2.say(); Per.go(); ~~~ # 面向对象继承 实现人类和中国人(重点) 与美国人(辅助了解) ## 实现人类 ~~~ function Person(name,age){  this.name = name;  this.age = age; } ​ Person.prototype.say = function(){  console.log('i can say '); } ~~~ ## 原型链继承 ~~~ ​ //原型链继承 ​ function Chinese(skin,language){  this.skin = skin;  this.language = language; } Chinese.prototype = new Person(); var p1 = new Chinese('黄色','中文') console.log(p1); p1.say(); ~~~ ### 图解 ![](https://box.kancloud.cn/513784702990bcf3e88ffa1338f18082_1063x611.png) ### 问题 Chinese构造函数的原型对象的constructor指向谁。它指向Person,原因是为什么因为new Person()身上没有constructor,它通过`__proto__`指向Person的原型对象(Person.prototype),这个对象的constructor指向Person。 构造函数的原型对象的constructor指向构造函数自身,按照这个原则我们这个代码或者关系图是有错误的。如何解决? 将new Person()手动添加constructor并且手动指向Chinese。即可修复。 ### 修改后的代码 ~~~ //原型链继承 function Chinese(skin,language){  this.skin = skin;  this.language = language; } var person1 = new Person(); person1.constructor = Chinese; Chinese.prototype = person1; var p1 = new Chinese('黄色','中文') console.log(p1); p1.say(); console.log(Chinese.prototype.constructor) ~~~ ### 完成后的图 ![](https://box.kancloud.cn/e6607791dca2e0bce7cd3239d6ba8231_1063x621.png) ## 构造函数继承(对象冒充继承) 上面的继承只能继承原型对象的方法。并不能继承父类的属性。如何解决?可以通过关键字call或者applay来实现当前类借用扩充。不属于原型或者原型链的操作方式。帮不需要画图。 ~~~ // 构造函数继承 对象冒充 function Chinese(skin,language,name,age){  this.skin = skin;  this.language = language;  Person.call(this,name,age); } var p1 = new Chinese('黄色','中文','小白',18); console.log(p1); ~~~ ## 组合继承 组合继承其实就是原型链接继承和构造函数继承的结合体 ~~~ //原型链继承 function Chinese(skin,language,name,age){ this.skin = skin; this.language = language; Person.call(this,name,age);// 第一次调用父类 } var person1 = new Person(); //第二次调用父类 person1.constructor = Chinese; Chinese.prototype = person1; var p1 = new Chinese('黄色','中文','小白',18); console.log(p1); p1.say(); ~~~ ### 问题 组合继承的问题在于在子类中调用父类一次。会导致性能浪费。 ![](https://box.kancloud.cn/a5d66c36dcdef7d29ac0a64f7e3d5114_836x229.png) ## 原型式继承(原型继承) ### 简易版 ~~~ function Chinese(skin,language,name,age){ this.skin = skin; this.language = language; } Chinese.prototype = Person.prototype; var p1 = new Chinese('黄色','中文') console.log(p1); p1.say(); function America(skin,language,name,age){ this.skin = skin; this.language = language; } Chinese.prototype = Person.prototype; var a1 = new Chinese('白色','英语') console.log(a1); a1.say(); ~~~ ### 图解 ![](https://box.kancloud.cn/337662c67140ef6630e1dfa20777ca40_1624x633.png) ### 问题 如果把Chinese.prototype.say方法修改会产生什么问题?修改代码尝试。 ~~~ Chinese.prototype.say = function(){ console.log('我是中国人') } ~~~ 完整代码如下 ~~~ function Chinese(skin,language,name,age){ this.skin = skin; this.language = language; } Chinese.prototype = Person.prototype; Chinese.prototype.say = function(){ console.log('我是中国人') } var p1 = new Chinese('黄色','中文') console.log(p1); p1.say(); function America(skin,language,name,age){ this.skin = skin; this.language = language; } Chinese.prototype = Person.prototype; var a1 = new Chinese('白色','英语') console.log(a1); a1.say(); ~~~ ### 执行结果 ~~~ Chinese {skin: "黄色", language: "中文"} 我是中国人 Chinese {skin: "白色", language: "英语"} 我是中国人 ~~~ 我们发现一旦对say方法进行修改,那么导致,继承于父类Person的所有子类的原型对象的say方法都发生改变。原因是因为他们用的同一份。如何解决? ### 改造原型继承 其实想法很简单,又要避开重复实例化父类多次,又要实现不同子类继承同一父类,在原型对象方法修改时不会影响其它子类。 **看下图,大家可以简单理解,其实中间圈中的部分不过就是搭了个桥,让子类修改原型时候,不会直接修改父类原型。而是创建一个新的空类,这个空类使用原型式继承父类原型对象。再通过实例化一个对象作为子类Chinese的原型对象。** **这样的话子类的实例 p 可以通过`__proto__`查找到父类 Chinese 的原型对象,同样的也可以通过原型对象的`__proto__`来查找到Person类的原型对象的公共方法。 这样原型链得到继承。** **同时我们修改子类Chinese的原型对象也仅仅是修改了f对象的功能。没有修改 父类Person类的原型对象。** **简单理解这个图的意思就就我们将父类和子类中间横插一脚。** ![](https://box.kancloud.cn/db9bd60c32e3c97f1abb6631c3f1119a_1107x720.png) ### 简化后的图 ![](https://box.kancloud.cn/ee91e6f6519c7d3ac6f33cc351a0b72e_675x272.png) ### 代码实现 ~~~ function Chinese(skin,language,name,age){ this.skin = skin; this.language = language; } // Chinese.prototype = Person.prototype; // 原型继承改造 //1.创建F构造函数,并且返回实例 new F(); function object(parentPro){ var F = function () {} F.prototype = parentPro; return new F(); } //将返回的实例作为子类的原型对象,同时修改子类原型对象的constructor指向子类。 function inhert(par,child) { var mid = object(par.prototype); mid.constructor = child; child.prototype = mid; } inhert(Person,Chinese); ~~~ ## 寄生式组合继承 其实就是原型式继承改造后和构造函数继承的结合版。 ~~~ // Chinese.prototype = Person.prototype; // 原型继承改造 //1.创建F构造函数,并且返回实例 new F(); function object(parentPro){ var F = function () {} F.prototype = parentPro; return new F(); } //2.将返回的实例作为子类的原型对象,同时修改子类原型对象的constructor指向子类。 function inhert(par,child) { var mid = object(par.prototype); mid.constructor = child; child.prototype = mid; } function Chinese(skin,language,name,age){ this.skin = skin; this.language = language; Person.call(this,name,age) } inhert(Person,Chinese); var p1 = new Chinese('黄皮肤','中文','小白',18) console.log(p1); p1.say(); ~~~ ![](https://box.kancloud.cn/f72d0892235df210e070ab0fa13d26e2_672x228.png) # class继承 ~~~ class Person{ constructor(name,age){ this.name = name; this.age = age; } say(){ console.log(this.name) } } class Chinese extends Person{ constructor(name,age,skin,language){ super(name,age); this.skin = skin; this.language = language; } } var p = new Chinese('小黑',18,'白','汉语') console.log(p); ~~~ ## 注意事项 如果子类要继承父类,子类的构造函数中必须书写super() 而且必须写在首行。它其实就相当于构造函数中的对象冒充写法。 ![](https://box.kancloud.cn/2fb6b8d0be4a510c16c52a9a45493064_608x263.png) ## 给父类添加静态方法 父类的静态方法可以给子类继承静态方法。 ~~~ class Person{ constructor(name,age){ this.name = name; this.age = age; } say(){ console.log(this.name) } static go(){ console.log('i can go') } } ~~~ ~~~ Person.go();//i can go Chinese.go();//i can go ~~~