JavaScript继承详解(Mixin)

7 3月

上一篇JavaScript继承详解(Klass)介绍了各种继承的模式。但究竟为何要继承?一个很重要的目的就是为了代码复用。因此还有一种非常简单粗暴的方式,直接无脑拷贝父对象的各属性。

Mixin的这种做法其实和OO语言里的继承没啥关系,是一种组合行为,本质上并不是基础,而是通过复制属性,从另一个对象中获得需要的功能

function extend(parent, child) {
    var i;
    child = child || {};
    for(i in parent) {
        if(parent.hasOwnProperty(i)) {
            child[i] = parent[i];
        }
    }
    return child;
}
var p1 = {name:"Jack"};
var c1 = extend(p1);
c1.name;	// Jack

上面是浅复制,遍历父对象的属性复制给子对象,但如果属性是数组或对象,只会浅复制属性:

var p2 = {
    counts: [1, 2, 3],
    interest: {read: true}
};
var c2 = extend(p2);
console.log(p2.interest === c2.interest);    // true

c2.counts.push(4);
console.log(p2.counts.toString());    // 1,2,3,4
c2.interest.play = true;
console.log(p2.interest.play);        // true

JS里浅复制的概念和传统OO语言里对指针的浅复制一样,并无二致。上例中在子类对象上,对数字或对象属性进行操作会影响到父类对象。通常这不是我们想要的效果,因此要改成深复制,即如果属性是数组或对象就深复制该属性:

function extend(parent, child) {
    var i;
    child = child || {};
    for(i in parent) {
        if(parent.hasOwnProperty(i)) {
	    if(typeof parent[i] === "object") {
                child[i] = (Array.isArray(parent[i])) ? [] : {};
                extend(parent[i], child[i]);
            } else {
                child[i] = parent[i];
            }
        }
    }
    return child;
}

var p3 = {
    counts: [1, 2, 3],
    interest: {read: true}
};
var c3 = extend(p3);
console.log(p3.interest === c3.interest);    // false

c3.counts.push(4);
console.log(c3.counts.toString());    // 1,2,3,4
console.log(p3.counts.toString());    // 1,2,3
c3.interest.play = true;
console.log(c3.interest.play);    // true
console.log(p3.interest.play);    // undefined

这种模式被广泛使用,Firebug的extend方法是浅复制,jQuery的extend是深复制。当然不论是浅复制还是深复制,并没有涉及原型,仅复制它们自身的属性。

Mixin

这种复制属性实现继承的思想可以进一步扩展成Mixin混入模式。并不完整地复制一个对象,而是从多个对象中复制出属性,将其组合成一个新对象

function mixin() {
    var i, prop, child = {};
    for(i=0; i<arguments.length; i+=1) {
        for(prop in arguments[i]) {
            if(arguments[i].hasOwnProperty(prop)) {
                child[prop] = arguments[i][prop];
            }
        }
    }
    return child;
}

你可以用它传递任意数量的对象:

var person = mixin(
    {gender:'male', location:'Shanghai'},
    {interest: "read travel"},
    {job: "IT"}
);
console.log(person);
//gender    "male"
//interest  "read travel"
//job	    "IT"
//location  "Shanghai"

总结

本文结合JavaScript继承详解(Klass),把继承介绍的差不多了。你能在各种库中找到各种模式的蛛丝马迹。当然有时候简单的代码复用其实没必要用继承,你只不过想借用别人的方法,并不希望与它们形成父子关系,那你用call和apply即可。

发表评论

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