标签:code 技术分享 class 函数传参 let log 组合 .com 重复
提到继承,我们常常会联想到C#、java等面向对象的高级语言(当然还有C++),因为存在类的概念使得这些语言在实际的使用中抽象成为一个对象,即面向对象。JavaScript这门语言本身就是作为浏览器脚本语言的弱语言,伴随着没有类的概念,JavaScript就成为了一种基于对象的语言而不是面向对象的语言,面向对象就会存在继承,那么基于对象的JavaScript是如何继承的,老生常谈一下。
(1)原型继承
function Parent() { this.name = "dqhan"; this.arr = [‘haha‘, ‘heihei‘, ‘hehe‘]; this.action = function () { console.log(‘Iamdqhan‘); }; } function Child() { //do someting } Child.prototype = new Parent(); //mark var child1 = new Child(); var child2 = new Child();
原型继承是最基础的继承方式,核心就是重写子类原型,是父类实例对象充当子类原型,
优点:通俗易懂
缺点:(1)原型中的引用类型共享(2)无法向父类构造函数传参
child1.arr.push(‘houhou‘);
console.log(child1.arr); // ["haha", "heihei", "hehe", "houhou"]
console.log(child2.arr); // ["haha", "heihei", "hehe", "houhou"]
原因就是操作child1中的arr时,首先回到child1下查找arr,当没有时会在child1的原型里查找,此时arr为child1原型中的属性,由于child2与child1的原型同为Parent的实例,此时更改arr值时,child2也会变,即原型共享。
那么如果现在更改属性name呢?
child1.name = "newdqhan";
console.log(child2.name); // "dqhan"
这又是为什么呢?因为child1.name会直接在child下重新定义一个name属性,这个name并不是原型上的属性,也就不会更改原型,如图。

(2)借用构造函数实现继承
function Parent(value) {
this.name = "dqhan";
this.arr = [‘haha‘, ‘heihei‘, ‘hehe‘];
this.value = value;
this.action = function () {
console.log(‘Iamdqhan‘);
};
}
function Child(value) {
Parent.call(this, value); //mark
}
var child1 = new Child("value1");
var child2 = new Child("value2");
child1.action === child2.action //false
看一下结果

利用Call的方式将Child方法中的this传入Parent方法中,然后重新定义一遍Parent中的属性及方法
Call方法我们这个换一种写法:
function Child(value) {
this.tempParent = Parent;
this.tempParent(value);
delete this.tempParent
}
在Child函数中引用Parent构造函数并执行,此时就会在Child中定义了一遍Parent中的属性及方法。看到了这里我相信你一定会产生一个疑惑就是为什么不在Child中直接执行一次Parent函数呢。
看一下结果

并没有达到理想的结果,这是为啥呢?因为在Child中不引用Parent,直接调用,Parent中的this默认为Window对象(严格开发模式下为Undefined),目的就是为了传递this。
function Parent() {
this.name = "dqhan"; //this 为 Window 对象
this.arr = [‘haha‘, ‘heihei‘, ‘hehe‘];
this.action = function () {
console.log(‘Iamdqhan‘);
};
}
function Child(){
Parent();
}
var child = new Child();
function Parent() {
"use strict";
this.name = "dqhan"; //this 为 undefined
this.arr = [‘haha‘, ‘heihei‘, ‘hehe‘];
this.action = function () {
console.log(‘Iamdqhan‘);
};
}
function Child(){
Parent();
}
var child = new Child();
优点:摒弃了原型,避免了原型共享;解决了向父类构造函数传参的问题。
缺点:没生成一个新对象,都会重新定义一次function,严重影响内存。
(3)组合继承:
这种继承方式相比第二种将父类方法中的函数定义在了原型内
function Parent() {
this.name = "dqhan";
this.arr = [‘haha‘, ‘heihei‘, ‘hehe‘];
}
Parent.prototype.action = function () {
console.log(‘Iamdqhan‘);
};
function Child() {
Parent.call(this);
}
Child.prototype = new Parent();
var child1 = new Child("value1");
var child2 = new Child("value2");
child1.action === child2.action // true
结果:

优点:这种方式成功的避免了重复定义function的尴尬情况,同时解决了原型共享的问题。
缺点:如果有两个子类继承父类,但是父类的属性有一个子类不用,怎么搞?这个是没法避免的,而且父类的属性全部在子类的原型上,很不美观。
(4)寄生组合继承:
为了扣掉组合继承中原型中不需要的属性,看到为了满足这一点,可不可以介样:
function Parent() {
this.name = "dqhan";
this.arr = [‘haha‘, ‘heihei‘, ‘hehe‘];
}
Parent.prototype.action = function () {
console.log(‘Iamdqhan‘);
};
function Child() {
Parent.call(this);
};
Child.prototype = Parent.prototype;
结果:

乍一看好像对,实际Child中的__proto__为Object,并不是Parent,已经背离了Child继承Parent的目的。
改进:
function Parent() {
this.name = "dqhan";
this.arr = [‘haha‘, ‘heihei‘, ‘hehe‘];
}
Parent.prototype.action = function () {
console.log(‘Iamdqhan‘);
};
function Child() {
Parent.call(this);
};
function initObject(obj) {
var
F = function () { };
F.prototype = obj;
return new F();
}
//Child.prototype = Parent.prototype;
var newPrototype = initObject(Parent.prototype);
newPrototype.constructor = Child;
Child.prototype = newPrototype;
var child1 = new Child("value1");
var child2 = new Child("value2");
结果:

Child的__proto__已经为Parent,而且去掉了原型上我们不要的属性只留下父类在原型上定义的方法,核心目的就是为了更改原型链的设置。
标签:code 技术分享 class 函数传参 let log 组合 .com 重复
原文地址:http://www.cnblogs.com/moran1992/p/6274032.html