相关文章导航
【JavaScript 面向对象编程——《深浅拷贝》】计数器例子
【JavaScript 面向对象编程——《设计模式》】计数器例子
👉【JavaScript 面向对象编程——《原型继承》】计数器例子👈
文章目录
一、原型链继承优缺点
二、借用构造函数继承优缺点
三、组合继承优缺点
四、原型式继承(1)普通原型式继承优缺点
(2)Object.create() 原型式继承优缺点
五、寄生式继承(1)普通寄生式继承优缺点
(2)Object.create() 寄生式继承优缺点
六、寄生组合式继承【最理想继承】(1)普通寄生组合式继承优缺点
(2)Object.create() 寄生组合式继承优缺点
一、原型链继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
function SuperType(n) {
this.num = n;
this.changeBy = function(val) {
this.num += val;
}
}
SuperType.prototype.add = function(val) {
this.changeBy(1);
};
SuperType.prototype.red = function(val) {
this.changeBy(-1);
};
SuperType.prototype.value = function(val) {
return this.num;
};
function SubType() {}
SubType.prototype = new SuperType(0);
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = new SubType();
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = new SubType();
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>原型链继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:子类的构造函数的原型对象,是父类的构造函数创建的实例;能通过 instanceOf 和 isPrototypeOf 的检测。 【优点】:拼接了原型链。子类的实例可以从父类继承属性/方法,子类的实例是父类的实例。 【缺点】:a)子类的构造函数无法向父类的构造函数传参。定义子类的构造函数时,继承就确定了,跟子类的构造函数的执行无关;b)子类的实例共享原型对象的属性/方法,互相干扰;c)不能实现多继承。 原型对象只能有一个;
二、借用构造函数继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
function SuperType(n) {
this.num = n;
this.changeBy = function(val) {
this.num += val;
};
}
function SubType(n) {
SuperType.call(this, n);
}
SubType.prototype.add = function() {
this.changeBy(1);
};
SubType.prototype.red = function() {
this.changeBy(-1);
};
SubType.prototype.value = function() {
return this.num;
}
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = new SubType(0);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = new SubType(0);
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>借用构造函数继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:在子类的构造函数中,通过call ( ) 或 apply ( ) 的形式,调用父类的构造函数来实现继承。 【优点】:a)解决了原型链继承方式中,子类的对象会共享父类构造函数的原型对象的问题;b)创建子类的对象时,可以向父类构造函数中传递参数;c)可以实现多继承(call / apply多个父类的构造函数); 【缺点】:a)创建的实例并不是父类的实例,只是子类的实例;b)没有拼接原型链,不能使用 instanceof。因为子类的实例只继承了父类的实例属性/方法,没有继承父类的构造函数的原型对象中的属性 / 方法;c)每个子类的实例都持有父类的实例方法的副本,浪费内存,影响性能,而且无法实现父类的实例方法的复用;
三、组合继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
function SuperType(n) {
this.num = n;
this.changeBy = function(val) {
this.num += val;
}
}
SuperType.prototype.add = function() {
this.changeBy(1);
};
SuperType.prototype.red = function() {
this.changeBy(-1);
};
function SubType(n) {
SuperType.call(this, n);
}
SubType.prototype = SuperType.prototype;
SubType.prototype.constructor = SubType;
SubType.prototype.value = function() {
return this.num;
};
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = new SubType(0);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = new SubType(0);
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>组合继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:组合继承仅仅是同时使用了原型链继承和构造函数继承;具体做法是,将父类的实例作为子类的构造函数的原型对象,并在子类的构造函数中调用父类的构造函数。 【优点】:a)既可以从父类的构造函数的原型对象继承方法,也能从父类的构造函数继承属性;b)既是父类的实例,也是子类的实例;c)拼接了原型链,支持 instanceof 、isPrototypeOf 检测;d)调用父类的构造函数时可以传参数;e)从父类的构造函数的原型对象继承的方法,可以被复用,被所有子类的实例共享; 【缺点】:父类的构造函数执行了两次,从而父类的实例属性被创建了两次,在子类的原型对象、子类的实例中都存在。父类的实例属性被继承了两次,子类的实例中的属性覆盖了子类的原型对象中的属性。浪费了内存和性能。
四、原型式继承
(1)普通原型式继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
var makeCounter = {
num : 0,
changeBy : function(val) {
this.num += val;
},
add : function() {
this.changeBy(1);
},
red : function() {
this.changeBy(-1);
},
value : function() {
return this.num;
}
};
function object(o) {
var F = function() {};
F.prototype = o;
return new F();
}
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = object(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = object(makeCounter);
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>原型式继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:利用工具函数,通过原型对象直接得到父类的实例,并当作子类对实例使用。 【优点】:不涉及父类的构造函数,不调用父类的构造函数就能实现继承。 【缺点】:方法在函数中定义,无法得到复用;本质上还是原型链继承,只是通过工具函数进行了封装,仍然存在子类的实例共享原型对象的问题。
(2)Object.create() 原型式继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
var makeCounter = {
num : 0,
changeBy : function(val) {
this.num += val;
},
add : function() {
this.changeBy(1);
},
red : function() {
this.changeBy(-1);
},
value : function() {
return this.num;
}
};
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = Object.create(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = Object.create(makeCounter);
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>"Object.create()" 实现原型式继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:在ES5中规范了 Object.create() 方法,可以用做工具函数。 【优点】:不涉及父类的构造函数,不调用父类的构造函数就能实现继承。 【缺点】:方法在函数中定义,无法得到复用;本质上还是原型链继承,只是通过工具函数进行了封装,仍然存在子类的实例共享原型对象的问题。
五、寄生式继承
(1)普通寄生式继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
var makeCounter = {
add : function() {
this.changeBy(1);
},
red : function() {
this.changeBy(-1);
},
value : function() {
return this.num;
}
};
function object(o) {
var F = function() {};
F.prototype = o;
return new F();
}
function createObj(o, n) {
var clone = object(o);
clone.num = n;
clone.changeBy = function(val) {
clone.num += val;
}
return clone;
}
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = createObj(makeCounter, 0);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = createObj(makeCounter, 0);
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>寄生式继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:与原型式继承完全相同,只是对父类的实例(也当作子类的实例使用)进行了增强。 【优点】:不涉及父类的构造函数,不调用父类的构造函数就能实现继承。 【缺点】:方法在函数中定义,无法得到复用;本质上还是原型链继承,只是通过工具函数进行了封装,仍然存在子类的实例共享原型对象的问题。
(2)Object.create() 寄生式继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
var makeCounter = {
add : function() {
this.changeBy(1);
},
red : function() {
this.changeBy(-1);
},
value : function() {
return this.num;
}
};
function createObj(o, n) {
var clone = Object.create(o);
clone.num = n;
clone.changeBy = function(val) {
clone.num += val;
}
return clone;
}
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = createObj(makeCounter, 0);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = createObj(makeCounter, 0);
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>"Object.create()" 实现寄生式继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:在ES5中规范了 Object.create() 方法,可以用做工具函数。 【优点】:不涉及父类的构造函数,不调用父类的构造函数就能实现继承。 【缺点】:方法在函数中定义,无法得到复用;本质上还是原型链继承,只是通过工具函数进行了封装,仍然存在子类的实例共享原型对象的问题。
六、寄生组合式继承【最理想继承】
(1)普通寄生组合式继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
function object(o) {
var F = function() {};
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType) {
var protoType = object(superType.prototype);
protoType.constructor = subType;
subType.prototype = protoType;
}
function SuperType(n) {
this.num = n;
this.changeBy = function(val) {
this.num += val;
}
}
SuperType.prototype.add = function() {
this.changeBy(1);
};
SuperType.prototype.red = function() {
this.changeBy(-1);
};
function SubType(n) {
SuperType.call(this, n);
}
inheritPrototype(SubType, SuperType);
SubType.prototype.value = function() {
return this.num;
};
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = new SubType(0);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = new SubType(0);
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>寄生组合式继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:用寄生继承来改造组合继承。 【优点】:具有组合继承的优点,同时只调用一次父类的构造函数,避免了内存和性能的浪费,消除了组合继承的缺点。 【缺点】:… 《高级程序设计》对寄生组合继承对评价:
(2)Object.create() 寄生组合式继承
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
function inheritPrototype(subType, superType) {
var protoType = Object.create(superType.prototype);
protoType.constructor = subType;
subType.prototype = protoType;
}
function SuperType(n) {
this.num = n;
this.changeBy = function(val) {
this.num += val;
}
}
SuperType.prototype.add = function() {
this.changeBy(1);
};
SuperType.prototype.red = function() {
this.changeBy(-1);
};
function SubType(n) {
SuperType.call(this, n);
}
inheritPrototype(SubType, SuperType);
SubType.prototype.value = function() {
return this.num;
};
function event(ele, btn1, btn2, obj) {
btn1.onclick = function() {
obj.add();
ele.innerHTML = obj.value();
}
btn2.onclick = function() {
obj.red();
ele.innerHTML = obj.value();
}
ele.innerHTML = obj.value();
}
window.onload = function() {
var Counter1 = new SubType(0);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
var Counter2 = new SubType(0);
var add2 = document.getElementById('add2');
var red2 = document.getElementById('red2');
var demo2 = document.getElementById('demo2');
event(demo2, add2, red2, Counter2);
}
</script>
</head>
<body>
<h3>"Object.create()" 实现寄生组合式继承
</h3>
<hr />
<p>计数器 1
</p>
<input type="button" id="add1" value="自增 (+1)" />
<input type="button" id="red1" value="自减 (-1)" />
<p id="demo1"></p>
<hr />
<p>计数器 2
</p>
<input type="button" id="add2" value="自增 (+1)" />
<input type="button" id="red2" value="自减 (-1)" />
<p id="demo2"></p>
</body>
</html>
优缺点
【原理】:在ES5中规范了 Object.create() 方法,可以用做工具函数。 【优点】:具有组合继承的优点,同时只调用一次父类的构造函数,避免了内存和性能的浪费,消除了组合继承的缺点。 【缺点】:… 《高级程序设计》对寄生组合继承对评价: