学事后端语言的同窗对继承并不陌生,可是对JS继承少量仍是有些困惑,不要试图问我是若是知道的,其实javascript继承主要是基于原型prototype
实现的。javascript
其实当你真正了解了原型链时候,再看js继承,其实比OOP语言更灵活、更简单一些。接下来咱们来看看原型链继承吧:java
//父类 function Animal(){} //子类 function Dog(){} //继承 Dog.prototype = new Animal();
其实,就是把子类的prototype
指向父类的实例,继承就完成了,很简单吧。这就是原型链继承
上面只是一个简单的继承结果,并没有实际意义,继承的目的就是要共享父类的属性和方法,接下来咱们一步一步来揭开这神秘的面纱git
/** * * 父类,带属性 * @constructor * @param name 名字 * @param type 动物分类 * @constructor */ function Animal(name,type) { this.name = name || 'your name'; this.type= type || 0; this.coatColor= ['white','block','yellow','brown']; //引用类型 //函数也是引用类型 this.speak = function () { console.log(this.name+' speaking .'); } } } /** * 为父类新增一个方法 * @returns {boolean} */ Animal.prototype.say= function () { console.log('my name is '+this.name); }; /** * 子类 * @constructor */ function Dog(name) { this.name = name; this.foot= 4; } //实现继承-原型链继承 => (子类 -> 子类原型->父类) ;继承 注意,继承必需要写在子类方法定义的前面 Dog.prototype = new Animal(); /** * 子类方法 * 为子类新增一个方法(在继承以后,不然会被覆盖/异常) dog.run is not a function */ Dog.prototype.run = function () { console.log('The '+ this.name +' was runing.'); }; var dog = new Dog('taiSen'); console.log(dog.name); //dog --子类覆盖父类的属性 console.log(dog.type); // 0 --父类的属性 console.log(dog.foot); //4 --子类本身的属性 dog.say(); //my name is taiSen --继承自父类的方法 dog.run(); //The taiSen was runing. --子类本身的方法
以上,看起来咱们好像已经完成了一个完整的继承了。可是,原型链继承有一个缺点
,就是属性
若是是引用类型
的话,会共享
引用类型 ,接下来我个Animal增长引用类型属性this.coatColor,测试下github
//测试下 var dog1= new Dog(); var dog2 = new Dog(); dog1.coatColor.push('blue'); console.log(dog1.coatColor); // [ 'white', 'block', 'yellow', 'brown', 'blue' ] console.log(dog2.coatColor); // [ 'white', 'block', 'yellow', 'brown', 'blue' ]
dog1,dog2 输出的coatColor同样,说明引用类型属性会被
全部实例共享
--- 这就是原型链继承的缺点,那么咱们若是解决这个问题呢? 接下来咱们就要借用————
构造函数继承
//子类 function Cat() { Animal.call(this) // 构造函数继承(继承属性) } //测试下 var cat1= new Cat(); var cat2 = new Cat(); cat1.coatColor.push('red'); console.log(cat1.coatColor); // [ 'white', 'block', 'yellow', 'brown', 'red' ] console.log(cat2.coatColor); // [ 'white', 'block', 'yellow', 'brown']
从结果看,咱们就解决了引用类型被全部实例共享的问题了。后端
注意
:这里跟原型链继承
有个比较明显的区别是并无使用prototype
继承,而是在子类里面执行父类的构造函数, 至关于把父类的代码复制到子类里面执行一遍,这样作的另外一个好处就是能够给父类传参。
测试代码:函数
/好比: function Pig(name) { Animal.call(this,name); } var pig1= new Animal('big Pig'); var pig2 = new Animal('small Pig'); console.log(pig1.name); // big Pig console.log(pig2.name); //small Pig
看起来是否是很像java,C#语言啊,以上构造函数解决了引用类型被全部实例共享的问题。测试
注意
: 正由于构造函数解决了解决了引用类型被全部实例共享的问题,致使了一个相对很矛盾的问题出现了,——————函数
也是 引用类型,函数也没办法共享了.也就是说,每一个实例里面的函数,虽然功能同样,可是却不是同一个函数,就至关于咱们每实例化一个子类,就复制了一遍的函数代码。
//父类新增this.speak函数 function Animal(name,type) { this.name = name || 'your name'; this.type= type || 0; this.coatColor= ['white','block','yellow','brown']; //引用类型 //函数也是引用类型 this.speak = function () { console.log(this.name+' speaking .'); } } //测试 console.log(pig1.speak===pig2.speak); // false
以上证实,父类的函数,在子类的实例下是不共享的。this
怎么办呢?,以上能够看出原型链继承 和 构造函数继承 这两种继承方式的优缺点恰好是互相矛盾的,那么咱们有没有办法鱼和熊掌兼得呢? 答案是确定的————组合继承prototype
// 父类 function Animal() { this.name = name || 'your name'; this.type= type || 0; this.coatColor= ['white','block','yellow','brown']; //引用类型 } // 父类函数 Animal.prototype.speak =function () { console.log(this.name+' speaking .'); } // 子类 function Chicken(){ Animal.call(this) // 构造函数继承(继承属性) } // 继承 Chicken.prototype = new Animal() // 原型链继承(继承方法)
继承方式 | 核心代码 | 优缺点 | 用法 |
---|---|---|---|
原型链继承 | Dog.prototype = new Animal() | 实例的引用类型共享 | 继承属性 |
构造函数继承 | 在子类Cat)里执行 Animal.call(this) | 会独享全部属性,包括引用属性(重点是函数) | 继承方法 |
组合继承 | 利用原型链继承要共享的属性,利用构造函数继承要独享的属性 | 实现相对完美的继承 | 结合上两位 |
本文中的代码见demo coding ,若是以为对您有用,帮加个star,万分感谢!code
今天就写到这,讲述了3种继承方式,其实J继承还有不少继承方式。其余留在下期再见咯。感受各位的收看,欢迎提问。