网站首页 » 前端开发 » JavaScript » JavaScript 面向对象的继承
上一篇:
下一篇:

JavaScript 面向对象的继承

什么是面向对象的继承

继承:在不影响影响父类功能及完整性的情况下,子类可以继承父类的一些功能。一个子类(派生类)可以继承自另一个父类(基类),派生类可以覆盖来自基类的方法。在基类中调用派生类对象的方法时,如果这个方法被派生类覆盖,那么应调用派生类的方法(多态)
JavaScript 会沿着原型链查找方法,一旦在派生类找到了同名方法,就不会使用基类的方法。

我们先来创建一个自定义对象(父类)

function Site(name,domain){
    this.name = name;
    this.domain = domain;
}
Site.prototype.say = function(){
    console.log(this.name);
}
var s1 = new Site("云库网","yunkus.com");
s1.say();

现在我们再来创建一个自定义对象(子类)

function MySite(name,domain,type){
    this.name = name;
    this.domain = domain;
    this.type = type;
}
var ms = new MySite("云库网","yunkus.com","前端开发");
console.log(ms); // MySite {name: "云库网", domain: "yunkus.com", type: "前端开发"}

你会发现子类里也有一个this.name = name;this.domain = domain的代码,跟父类中的代码是一样的,于是我们就可以
像下面这样写,来继承父类,以便使用父类的一属性和方法。

拷贝继承

属性的继承

属性的继承:调用父类的构造函数并用call 来更改执行的上下文,属性的继承很简单你可以像下面这样写来继承父类中的属性

function Site(name,domain){
    this.name = name;
    this.domain = domain;
}
Site.prototype.say = function(){
    console.log(this.name);
}
function MySite(name,domain,type){
    Site.call(this,name,domain); // 调用父类方法,同时改变执行的上下文(借用构造函数)
    this.type = type;
}
var ms = new MySite("云库网","yunkus.com","前端开发");
console.log(ms); // MySite {name: "云库网", domain: "yunkus.com", type: "前端开发"}

方法的继承

下面是继承思路的方法

function Site(name,domain){
    this.name = name;
    this.domain = domain;
}
Site.prototype.say = function(){
    console.log(this.name);
}
function MySite(name,domain,type){
    Site.call(this,name,domain); // 调用父类方法,同时改变执行的上下文(借用构造函数)
    this.type = type;
}
MySite.prototype = Site.prototype;
var ms = new MySite("云库网","yunkus.com","前端开发");
ms.say(); // 云库网

但是像上面那样写(给子类 MySite 添加方法)是会有问题的请看:

function Site(name,domain){
    this.name = name;
    this.domain = domain;
}
Site.prototype.say = function(){
    console.log(this.name);
}
var s1 = new Site("云库前端","http://yunkus.com");
console.log(s1)
function MySite(name,domain,type){
    Site.call(this,name,domain); // 调用父类方法,同时改变执行的上下文(借用构造函数)
    this.type = type;
}
MySite.prototype = Site.prototype;

// MySite 添加方法
MySite.prototype.owner = function(){
    console.log("这个网站属于朝夕熊");
}
var ms = new MySite("云库网","yunkus.com","前端开发");
console.log(ms);
ms.say();

然后我们查看下父类与子类里的一些属性和方法如下图:

面向对象的继承

你会有意外的收获,父类也同时拥有了这个 owner 方法。我们本意只想给 MySite 这个子类的原型添加 owner 方法;但是父类也自动添加了,这个问题的根源就在于对象之间的赋值是址传递的,所以就会出现这种情况。所以在把父类的方法赋值给子类的时候我们有必要做一个处理:对象拷贝复制继承。

下面是一个对象的拷贝方法,我们可以通过这个方法来实现子类继承父类的场景。

function copy (obj1,obj2){
    for(var attr in obj2){
        obj1[attr] = obj2[attr];
    }
}

下面我们就用这个 copy 方法实现对象的拷贝替代 MySite.prototype = Site.prototype;

function Site(name,domain){
    this.name = name;
    this.domain = domain;
}
Site.prototype.say = function(){
    console.log(this.name);
}
var s1 = new Site("云库前端","http://yunkus.com");
console.log(s1)
function MySite(name,domain,type){
    Site.call(this,name,domain); // 调用父类方法,同时改变执行的上下文(借用构造函数)
    this.type = type;
}
copy(MySite.prototype,Site.prototype);
function copy (obj1,obj2){
    for(var attr in obj2){
        obj1[attr] = obj2[attr];
    }
}
MySite.prototype.owner = function(){
    console.log("这个网站属于朝夕熊");
}
var ms = new MySite("云库网","yunkus.com","前端开发");
console.log(ms);
ms.say();

给子类添加了 owner 方法后父类也没有被污染到,这也是我们最终想要的结果,如下图:

面向对象的继承

这里有一个关于面向对象的拷贝继承的 DEMO:http://yunkus.com/demo/inheritance-in-javascript/。使用父类对象创建的实例没有位移限制,而使用继承了父类对象的子类对象创建的实例就单独添加了限制位移的代码。这个例子只是想说明一点,我们可以继承父类里的属性和方法,并且可以在此基础上添加或者修改属性和方法。通过借用构造函数和原型链实现组合继承。使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现属性的继承。

类式继承

在 JavaScript 中是没有类的概念的,但是我们可以把 JavaScript 中的构造函数看作是类。这种方法的基本思路是:在继承属性和方法时,分开继承。和前面说到的拷贝继承套路差不多。可以说是非常地像,只不过走的路子不一样而已,大体的思路是一样的。拷贝继承为了修改子类时不影响父类对方法拷贝了一份到子类,而类式继承则是通过一个清白的 Fn 对象来保存父类的方法,所以在子类调用方法时,就会沿着原型链到这个 Fn 里找。

window.onload = function(){
    var s1 = new Site(["云库网","云库前端"]);
    s1.say();
    var ms = new MySite(["云库网","云库前端"],"yunkus.com");
    ms.sayDomain();
    console.log(s1);
    console.log(ms);
}

// 类式继承
function Site(name){
    this.name = name;
}
Site.prototype.say = function(){
    console.log(this.name);
}

function MySite(name,domain){
    Site.call(this,name); // 继承属性,call(this)为更改执行上下文(借用构造函数)
    this.domain = domain;
}

var Fn = function(){};
Fn.prototype = Site.prototype;
// 上面这两行代码作用是:避免属性继承,而只继承方法

MySite.prototype = new Fn();
MySite.prototype.constructor = MySite; // 修正指向问题

MySite.prototype.sayDomain = function(){
    console.log(this.domain);
}

属性和方法分开继承,属性使用老套路实现,而方法则比较含蓄转了个弯通过一个清白的 Fn 作为中间人,当我们要给子类添加方法时,这些方法是挂载到 Fn(new Fn()) 的实例下的,而要继承父类的方法也是通过它来从原型链上找到(Site.prototype.say 方法)。

原型式继承

看到名字我也也大概知道他的实现方式了。不同于上面的继承方式,原型式继承直接真真正正的通过继承原型来实现的。我们来看看下面的代码:

function object(o) {
    function F(){};
    F.prototype = o;
    return new F();
}

var site = {
    name:"网站名",
    domain:["中文域名","英文域名"]
};

var mySite = object(site);
mySite.type = "前端开发";
mySite.say = function(){
    console.log("云库网");
}
console.log(mySite.domain);
mySite.say();

这里给子类添加属性和方法时有点不一样,这种实现方法是直接挂载到实例上的。

到此,我们就实现了子类继承父类属性和方法。好的继承可以减少代码量,提高项目的可维护性,所以你还等什么。

  • 微信扫一扫,赏我

  • 支付宝扫一扫,赏我

声明

原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

本文永久链接:http://yunkus.com/inheritance-in-javascript/

发表评论

电子邮件地址不会被公开。 必填项已用*标注

评论 END