上一篇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即可。