javascript继承(三)—继承的实现原理

打算针对js的继承写一系列文章,详细的分析js里继承原理,实现方式,各类继承方式的优缺点,以及最优继承方案,还有多继承的问题等….javascript

面向对象的编程的核心是封装、继承和多态,js能够看做是一种面向对象的语言,而面向对象的扩展性最核心的部分是多态,多态的必要条件有三个,首先就是继承,其次父类的引用指向子类,最后是方法重写。对于js来讲,因为其建立对象的方式多种多样,所以,须要对父类的多种属性和方法实现很好的继承,就必须找到一个比较完善的方法。本篇文章首选介绍三种最基本的继承方式,并分析这几种继承方式的缺陷。html

介绍js继承前,你们先须要js里类的各类属性以及js建立对象的几种模式有所了解。java

js类的属性能够参考: javascript中类的属性研究 这篇文章。编程

js建立对象的方式能够参考:javascript建立对象的三种模式 这篇文章。数组

第一种方式:对象冒充app

对象冒充,是指将父类的属性和方法一块儿传给子类做为特权属性和特权方法。函数

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayHi = function(){
        alert('hi');
    }
}

Person.prototype.walk = function(){
    alert('walk.......');
}

function Student(name,age,grade){
    this.newMethod = Person;
    this.newMethod(name,age);
    delete this.newMethod;
    this.grade = grade;
}

var s1 = new Student('xiaoming',10,3);
console.log(s1.name,s1.age,s1.grade);//xiaoming 10 3
//s1.walk();//s1.walk is not a function

可见Student类只继承了Person类的特权属性和方法,并无继承Person类的共有属性和方法。this

第二种方式:call或applyspa

使用call或apply改变对象的做用域来实现继承,让父类的this等于新建立的子类的对象(由于call和apply继承实现机制是同样的,就是传参方式不一样,call传多个参数用逗号隔开,apply用数组),本文主要介绍call来实现继承。prototype

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayHi = function(){
        alert('hi');
    }
}

Person.prototype.walk = function(){
    alert('walk.......');
}

function Student(name,age,grade){
    Person.call(this,name,age);
    this.grade = grade;
}

var s1 = new Student('xiaoming',10,3);
console.log(s1.name,s1.age,s1.grade);//xiaoming 10 3
//s1.walk();//s1.walk is not a function

同第一种问题同样,没有继承共有属性和方法。call改变了Person中this的做用域,使其指向了Student。对于call方法举例以下:

function Person(){
     this.name ='xiaoming';
}

Person.call(this);
alert(window.name);

此例将Person中this的做用域扩大到window上,使得Person中的name属性变为一个全局变量。

第三种方式:prototype

使用prototype属性实现继承,让父类的prototype赋给子类的prototype,也能够将父类的实例赋给子类的prototype,这里先介绍将父类的原型赋给子类的原型这种方式,并探讨这种方式的缺陷。在之后会着重介绍prototyp这种继承方式。

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayHi = function(){
        alert('hi');
    }
}

Person.prototype.walk = function(){
    alert('walk.......');
}

function Student(name,age,grade){   
    this.grade = grade;
}

Student.prototype = Person.prototype;

var s1 = new Student('xiaoming',6,3);
s1.walk();//walk.......
console.log(s1.name,s1.age,s1.grade);//xiaoming 6 3
console.log(s1.constructor); // Person(name,age)
Student.prototype.study = function(){
    alert('I am study');
}
var p1 = new Person();
p1.study();//I am study

主要缺陷:不能继承父类的特权属性和特权方法,子类的构造函数变成了Person(name,age),直接致使修改子类的原型方法时,父类也跟着修改了,耦合度过高了。

若是将父类的实例指向子类的原型会出现什么状况呢?

function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayHi = function(){
        alert('hi');
    }
}

Person.prototype.walk = function(){
    alert('walk.......');
}

function Student(name,age,grade){   
    this.grade = grade;
}

Student.prototype = new Person();

var s1 = new Student('xiaoming',6,3);
s1.walk();//walk.......
console.log(s1.name,s1.age,s1.grade);//undefined undefined 3
console.log(s1.constructor); // Person(name,age)
Student.prototype.study = function(){
    alert('I am study');
}
var p1 = new Person();
//p1.study();// p1.study is not a function

虽然子类Student的实例s1仍然指向父类Person的构造函数,但此时修改子类的共有方法并不会对父类有所影响。而后这种方式存在一个更为严重的问题是,子类虽然继承父类的特权属性,可是无法进行修改。而且每建立一个子类的实例时都会把父类的全部属性和方法建立一遍,相对于继承父类的prototype属性中共有方法使用同一代码块对代码空间存在较为严重的浪费。

总结:几种继承方式各有各的缺陷,那么如何实现完美的继承呢。也许将其中的某两种继承方式结合起来才行。在之后会接着介绍call和prototype结合实现js的继承功能。

相关文章
相关标签/搜索