javascript 中的类继承模式
发布在javascript模式2014年3月19日view:4639
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

类继承模式1

在javascript中默认的继承模式是将子类的构造函数的prototype设置为父类的实例(注意 这里的prototype是一个实例,不是构造函数)

function inherit(C, P) {
    C.prototype = new P();    
}

function Parent(name) {
    this.name = name || "parent";
    this.say = function () {
        alert(this.name);
    }
}

function Child(name) {
}

inherit(Child, Parent);

var child = new Child("test");

child.say() // "parent"

中继承模式存在2个缺点: - 会把父类实例中的所有属性继承下来,例如上述代码中parent的name属性是不该被继承的。 - 不能传递参数,上面的例子中child.say()应该是要输出test

类继承模式2 —-借用构造函数

该模式通过在子构造函数中调用父类的构造函数达到传递参数的效果

function Parent(name) {
    this.name = name || "parent";
    this.say = function () {
        alert(this.name);
    }
}
Parent.prototype.getName = function () {
    return this.name;    
}

function Child(name) {
    Parent.apply(this, arguments);   
}

var child = new Child('test');
child.say(); // test

好,这里解决了参数传递的问题,同时Parent的name属性也不会被继承,细心的你可能发 现了Parent的prototype没被Child继承,导致child.getName()报错。

为了解决这个问题,我们加上下面的代码:

Child.prototype = Parent.prototype;

这样问题就解决啦~

实际上这样还是有一个缺点的,这里实际上共享了原型prototype, 如果一个子类修改了Child.prototype.getName = function () {}, 那么所有子类都会受到牵连。

类继承模式3 —-终极解决方案

要解决第二种方法中的共享原型的问题,我们可以通过设置一个空白的中介来作为Child的 prototype,而该中介的prototype为Parent的prototype,对于参数传递的问题,我们在父 类中添加一个init函数来处理参数,我们通过代码来看怎么解决。

function Parent(name, age) {
    this.init.apply(this, arguments);
}

Parent.prototype = {
    // 通过init解决参数传递的问题
    init: function (name, age) {
        this.name = name;
        this.age = age;
    },
    say: function () {
        alert('Hi, 我是' + this.name + ", 我今年" + this.age + "岁。")    
    }
}

// 继承辅助函数
var inherit = function () {
    var F = function () {};
    return function (C, P) {
        F.prototype = P.prototype;
        C.prototype = new F();
        C.prototype.constructor = C; // 注意constructor设置为子类的构造函数
        C.parentClass = P.prototype; // 方便调用父对象中的方法
    };
}();

function Child(a, b) {
    this.init.apply(this, arguments); 
}
inherit(Child, Parent);

var child0 = new Child('test', '19');
child0.say(); // Hi,我是test,我今年19岁
Child.prototype.say = function () {
    alert("error...");
};

function AnotherChild(a, b) {
    this.init.apply(this, arguments); 
}
inherit(AnotherChild, Parent);

var child1 = new AnotherChild('test2', '20');
child1.say(); // Hi,我是test2,我今年20岁

在代码中我修改了原型prototype中的say函数,但是并没有影响到其他子类的say函数, 这是因为我们每个child的实例都拥有一个单独的F的实例作为原型,如果你真的想影响到 其他子类的话,你可以这么改Child.prototype.__proto__.say = function () {},但是 正常情况想__proto__是不被开放访问的,所以这个实际上是很安全的。

最后,有一点要强调,就是共享的方法写在prototype中。

终于写完了,要是只写代码不用写文字就好了,以上纯粹是自己在学习中的一些想法, 如果有什么不正确的地方,求指正,一起学习。

评论
发表评论
5年前
赞了此文章!
6年前

@辉泉__听奶奶的话 就是你上面的例子中,child本来应该具有一样的行为,你把中间添加一个空对象做prototype,这样就可以child1的say和child2的say行为不一致,我看不出这样有什么好处。

6年前

@福气鱼 “但是有一点我不清楚,为什么要为一个类的某一个特殊对象添加特别功能?一个类产生的对象不该具有一样的行为吗?” 不明白你问什么~

6年前

@辉泉__听奶奶的话 就是F.prototype = P.prototype; C.prototype = new F();这里

6年前

@福气鱼 那个地方?

6年前

但是有一点我不清楚,为什么要为一个类的某一个特殊对象添加特别功能?一个类产生的对象不该具有一样的行为吗?

6年前

我一句话来总结下这个文章就是没有将Child的prototype直接设置成Parent类的对象或者说Parent的prototype。而是在中间夹了一个空对象作为缓冲,这样当我们给具体某个Child的prototype添加内容时,不会影响其它的Child,如果我们要给所有Child添加内容时还是要去修改Parent的prototype对象。

6年前

@码砖专业户 想了下是应该声明另一个构造函数,然后继承的,本意表达错了

6年前

function Parent(name, age) { this.init.apply(this, arguments); }

Parent.prototype = { // 通过init解决参数传递的问题 init: function (name, age) { this.name = name; this.age = age; }, say: function () { alert('Hi, 我是' + this.name + ", 我今年" + this.age + "岁。")
} }

// 继承辅助函数 var inherit = function () { var F = function () {}; return function (C, P) { F.prototype = P.prototype; C.prototype = new F(); C.prototype.constructor = C; // 注意constructor设置为子类的构造函数 C.parentClass = P.prototype; // 方便调用父对象中的方法 }; }();

function Child(a, b) { this.init.apply(this, arguments); } inherit(Child, Parent);

var child0 = new Child('test', '19'); child0.say(); // Hi,我是test,我今年19岁 Child.prototype.say = function () { alert("error..."); };

var child1 = new Child('test2', '20'); child0.say(); // 结果error... child1.say(); // 结果error...

6年前

@辉泉__听奶奶的话

这些忘记纠正了

在代码中我修改了原型prototype中的say函数,但是并没有影响到其他子类的say函数, 这是因为我们每个child的实例都拥有一个单独的F的实例作为原型
child1.say(); // error......
6年前

@码砖专业户 多谢提醒,已改正。终于有人认真看的了,好开心

6年前

这里应该是个立即执行的匿名函数

var inherit =(function () {
    var F = function () {};
    return function (C, P) {
        F.prototype = P.prototype;
        C.prototype = new F();
        C.prototype.constructor = C; // 注意constructor设置为子类的构造函数
        C.parentClass = P.prototype; // 方便调用父对象中的方法
    };
})();

实例是没有prototype属性的,只有函数(function)才有的

var child0 = new Child('test', '19');
child0.say(); // Hi,我是test,我今年19岁

Child.prototype.say = function () {
    alert("error...");
};

var child1 = new Child('test2', '20');
child1.say(); // Hi,我是test2,我今年20岁

请多多指教

6年前

@ucdream 这个事ECMAScript5的函数,有些浏览器还不兼容。。。

6年前

使用object.create(P.prototype)就避免了使用空函数

6年前

最后的代码中是不是写掉了0和1?

PUBLISHED IN
javascript模式

学习使用javascript高大上的设计模式

我的收藏