对于js继承一直都是半懂半不懂的状态,感觉需要整理一下:
参考自:
JavaScript原型继承工作原理
JavaScript实现继承的几种方式
JavaScript继承方式详解
1. 区分类继承和实例化的差别
非常常用的类继承是这个样子的:
B.prototype = new A()
这时候特别容易和实例化给混淆了(反正我混了—):
b = new A()
感觉不好玩了对不对,这个时候开始好奇new到底干了什么事情(理解原理非常重要的撒):
创建类的示例 初始化 返回实例
用代码表示如下:
function New (f) {
var n = { '__proto__': f.prototype }; /*第一步*/
return function () {
f.apply(n, arguments); /*第二步*/
return n; /*第三步*/
};
}
还是有点云里雾里,怎么区分什么时候是类继承,什么时候是实例化呢?
呐,首先我先想到一切皆为函数,所以无论是实例化还是继承,本质上都是拥有A的属性(函数或者值)嘛,所以必须要想清楚js是怎么查找一个东西的属性的哇:
当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。
用代码表示如下:
function getProperty(obj, prop) {
if (obj.hasOwnProperty(prop)){
return obj[prop]
}else if (obj.__proto__ !== null){
return getProperty(obj.__proto__, prop)
}else{
return undefined
}
}
恩,这下子可以解释两者区别了:
B.prototype = new A()
: B.prototype要找自己属性的时候:先看看自己有没有 --> 看看自己的proto(也就是A.prototype)有没有 --> 一路往上
b = new A()
: b找自己属性的时候:先看看自己有没有 --> 看看自己的proto(也就是A.prototype)有没有 --> 一路往上
n年前收集了一张图,仔细看,就是我前面整理的内容了(不要晕,仔细看,此图博大精深):
对于原型链不理解的其实可以去看Typescript,对比Typescript和它生成出来的js代码,就容易理解了。
2. js继承方式
理解第一节以后,js继承方式什么的,so easy啦~
心中只要记住,作为子类的我,只要能够拥有父类prototype里的属性,就可以算作继承了(个人理解,觉不对可举正)
感觉可以用生孩子的例子贯穿这一节嘛~
-
原型链继承(其实就是最开始1的那种继承)
//父类 var Animal = function(){ //可以在构造函数里面直接设置属性~ this.name = 'animal'; } //也可以通过prototype Animal.prototype.say = function(){ console.log('Animal here'); } //子类 var Dog = function(){ } Dog.prototype = new Animal(); //改写父类prototype Dog.prototype.name = 'dog'; //创建实例 var doge = new Dog(); console.log(doge.name) //'dog'; doge.say() //'Animal here';
最传统的办法了,自己生
-
构造继承(借用方法)
//父类还是一样样的 var Dog = function(){ Animal.call(this); this.name = 'dog'; } //创建实例 var doge = new Dog(); console.log(doge.name) //'dog'; doge.say() //error;
咦,哪里不对,怎么会error说没有这个方法?!!
仔细看代码,要理解,Dog只是借用了Animal的构造方法,Animal的say方法是在prototype上的,当然找不到了,所以这种方法的问题就很明显啦,就是并没有依靠原型链关系,就像试管婴儿嘛,我只是借个肚子生小孩而已,基因还是我的(哈哈哈) 也就是说如果通过instanceof检查doge和Animal的关系,会发现返回没有关系的呦。
-
实例继承
var Dog = function(){
var dog = new Animal();
dog.name = 'dog';
return dog;
}//创建实例 var doge = new Dog(); console.log(doge.name) //'dog'; doge.say() //'Animal here'; console.log(doge instanceof Animal) // true console.log(doge instanceof Dog) // false
额,就好象小明爸爸借隔壁老王过来生小明~~ 实例是父类的实例,而不是子类的实例。
-
拷贝继承(暴力继承)
var Dog = function(){
var animal = new Animal();
for(var attr in animal){
this[attr] = animal[attr];
}
this.name = 'dog';
}//创建实例 var doge = new Dog(); console.log(doge.name) //'dog'; doge.say() //'Animal here'; console.log(doge instanceof Animal) // false console.log(doge instanceof Dog) // true
最野蛮的办法:把老王家的儿子抓过来,照着他的样子自己生一个
-
组合继承
var Dog = function(){
Animal.call(this, arguments);
this.name = 'dog';
}
Dog.prototype = new Animal();//创建实例
var doge = new Dog();
console.log(doge.name) //'dog';
doge.say() //'Animal here';
console.log(doge instanceof Dog) // true
console.log(doge instanceof Animal) // true
相当于借A肚子生孩子,完事拉住孩子和A的手说:我是你爸,你也来自A。好处是解决了实例继承的问题,坏处嘛,就是叫了两遍A。
- Object.create(proto[, propertiesObject])
ES5引入,我看着underscore用的主要也是这个办法。
代码原理解释:
Object.create = function (parent) {
function F() {}
F.prototype = parent;
return new F();
};
不多解释了,至于怎么生,这个我真编不出来了,有点像建个工厂,要生娃的基因给我,出来就是你娃。
其实说多了,个人觉得重在理解,目标是生娃,至于怎么生,就要看清好处坏处了。
3. 关于创建对象模式的一点嗑叨
什么是创建对象模式?(看到模式就犯头疼)额,说人话就是实例化,常见的就是var a = new A()
。
以前还小的时候看各种模式什么的,晕头转向,浪费时间也浪费脑力。
所以推荐先实践(酒肉穿肠过,佛祖心中流)实践多了,就会发现模式不模式,自己平时就在用。
//其实就是好累好累的,下次再补了—
---------此处为捡节操分割线----------
最近面试发现自己表达能力很不行,不知道写出来的博客会不会也整晕别人了== 欢迎举正~