javaScript面向对象继承
由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念。所以,要想实现继承,可以用js的原型prototype机制或者用apply和call方法去实现在面向对象的语言中,我们使用类来创建一个自定义对象。然而js中所有事物都是对象,那么用什么办法来创建自定义对象呢?这就需要用到js的原型:
我们可以简单的把prototype看做是一个模版,新创建的自定义对象都是这个模版(prototype)的一个拷贝 (实际上不是拷贝而是链接,只不过这种链接是不可见,新实例化的对象内部有一个看不见的__Proto__指针,指向原型对象)。
js可以通过构造函数和原型的方式模拟实现类的功能。 另外,js类式继承的实现也是依靠原型链来实现的。
<!DOCTYPE html><html><head><meta charset="UTF-8"><title></title><script>function Parent() {this.pv = "父类";}Parent.prototype.parentSay = function() {alert(this.pv);}function Son() {this.sv = "子类"}/*** 放子类继承父类,使用原型链来实现继承* 原型链继承就是将子类的原型指向父类实例*/var p1 = new Parent();Son.prototype = p1;Son.prototype.sonSay = function() {alert(this.sv);alert(this.pv);}/*** 方法的重新(覆盖)*/Son.prototype.parentSay = function() {alert("继承")}var s1 = new Son();s1.sonSay();s1.parentSay();var s2 = new Son();console.info(s2)</script></head><body></body></html>
当子类的原型指向父类的对象后,子类就继承了父类,实现了继承,这就是基于原型链的继承..同时使用原型链实现继承需要注意以下几个问题: 1、不要在设定原型链之后,再原型重写 2 一定要在原型链赋值之后才能添加或者覆盖方法.
父类方法的覆盖(重写)
当子类继承父类后,子类如果认为父类的方法不能满足自己或者不太满意父类的方法,可以使用与父类同名的方法来覆盖(重写)父类的方法.注意:javaScript中存在重写,但是没有重载.
子类初始化父类属性
基于原型链的继承我们说了缺陷就是无法实现子类去调用父类的构造函数,这洋就无法修改父类的属性,但是基于伪装就解决了这个问题.<!DOCTYPE html><html><head><meta charset="UTF-8"><title></title><script>function Parent(name,age) {this.pv = "父类";this.name = name;this.age = age;}Parent.prototype.parentSay = function() {alert(this.name+"=="+this.age);}function Son(name,age) {//Parent.apply(this,arguments)Parent.call(this,name,age); //===> Parent();this.sv = "子类"}Son.prototype.sonSay = function() {alert(this.sv);alert(this.pv);}var s1 = new Son("吕帅哥",23);alert(s1.pv);s1.parentSay();</script></head><body></body></html>
伪装解决了基于原型链的问题,但是也存在一些问题: 1. 因为使用伪装方式继承,子类原型不会指向父类,所以父类中写在原型的方法不会被子类继承,所以子类调用不到父类的方法. 2. 解决的方法就是将父类的方法放到子类中去.但是这又违背了封装的概念.
终极方案--基于组合实现继承
<!DOCTYPE html><html><head><meta charset="UTF-8"><title></title><script>/*** 基于组合的继承(原型链和伪装)*/function Parent(name,age) {this.name = name;this.age = age;}Parent.prototype.parentSay = function() {alert(this.name+"----"+this.age);}function Son(name,age,sex) {Parent.call(this,name,age);this.sex = sex;}Son.prototype = new Parent();Son.prototype.sonSay = function() {alert("哈哈");}var s1 = new Son("123",16,"男");s1.sonSay();s1.parentSay();</script></head><body></body></html>
<html><head><meta charset="UTF-8"><title></title><script>/** ES6之前,javascript没有方法描述类,我们只能构造函数来模拟类,但是* 它一直将class作为一个保留字存在* 在ES6中,javascript可以定义类* * class 类名 {* * constructor(属性列表) {* this.属性1 = 属性1;*this.属性2 = 属性2;*…… *}* *方法名称(参数列表) {* //方法体* * }* * }* *//*** 定义一个类,人类*/class Person {constructor(name,age,sex) {this.name = name;this.age = age;this.sex = sex;}say(msg) {alert(msg);}showInfos() {alert(this.name+"---"+this.age+"----"+this.sex);}/*** 静态方法*/static show(msg) {alert(msg);}}/*** 语法糖*/var p1 = new Person("萝叔叔",16,"男");//p1.say("嘿嘿,果然是个大帅哥");//p1.showInfos();////Person.show("嘿嘿");class Children extends Person {constructor(name,sex,age,height) {super(name,age,sex); //在子类调用父类的构造函数,必须放在首行this.height = height;}}var s1 = new Children("123","男",5,150);s1.say("呵呵,我是子类");s1.showInfos();</script></head><body></body></html>
<!DOCTYPE html><html><head><meta charset="UTF-8"><title></title><script>/*** 使用class关键字申明一个类,类名为Parent*/class Parent {//constructor方法就是Parent的构造方法//可以使用它来初始化Parent类的属性constructor(name,age) {this.name = name;this.age = age;}//直接申明Parent类的方法,saysay() {return this.name +"---->"+this.age;}//使用static关键字申明静态方法//注意静态方法属于类,而不属于对象static sayHell() {alert("Hello 刘帅哥");}}//错误//new Parent().sayHello();//正确,该方法数据类//Parent.sayHell();//let p1 = new Parent("阿里巴巴",20);////alert(p1.say())//使用extends来申明Son类继承Parent类class Son extends Parent {constructor(name,age,sex) {//使用super关键字表示父类(超类)super(name,age);this.sex = sex;}sayWord() {alert(this.sex+"----->"+this.name+"----------"+this.age);}//使用父类中的同名方法,会覆盖父类方法(override)say() {return "哈哈";}}let p1 = new Son("阿里巴巴",15,"男");//p1.sayWord();alert(p1.say());</script></head><body></body></html>