想要了解JavaScript中的原型继承,需要先知道原型和继承这两个概念。
1.原型
1.1原型关系的检测
isPrototypeOf方法用于测试一个对象是否存在于另一个对象的原型链上。
instanceof运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
function Fn(){}
function Fun(){}
var f = new Fn()
console.log(f.__proto__ === Fn.prototype); //t
console.log(Fn.prototype.isPrototypeOf(f)) //t
console.log(Fun.prototype.isPrototypeOf(f)) //f
console.log(Object.prototype.isPrototypeOf(f)) //t
console.log(f instanceof Fn); //t
console.log( f instanceof Fun ); // f
console.log( f instanceof Object ); // t
1.2原型的应用
当我们给fn的原型身上添加一个方法show,那么在通过new fn()创造出来的实例身上,都可以使用show这个方法。
function fn(){
this.haha = "CSDN"
}
fn.prototype.show = function(){
console.log(this.haha)
}
var f1 = new fn();
var f2 = new fn();
console.log(f1 === f2); //false
f1.show(); //CSDN
f2.show(); //CSDN
以下代码通过原型实现了,任何一个数组都可以使用noRepeat这个自定义的方法实现数组去重。
Array.prototype.noRepeat = function(){
var m = []
for(var i =0;i<this.length;i++){
if(m.indexOf(this[i])== -1){
m.push(this[i])
}
}
return m;
}
var arr = [4,4,3,5,8,6,9,5,5,6,7,0,2]
var res = arr.noRepeat();
console.log(res)
var arr1 = ["a","b","c","b","a"]
var res1 = arr1.noRepeat();
console.log(res1)
2.继承
所谓继承就是:让一个没有某个属性或方法的对象能够使用另一个具有某个方法或属性的对象的属性或方法。
var obj = {
name:"obj",
show:function(){
console.log(this.name)
}
}
var obj2 = {
name:"obj2"
}
obj.show() //obj
obj2.show(); //报错
以上obj2中没有show方法,强行使用则会报错。那么我们如何让obj2也能使用show方法呢?
这时候 我们的原型继承就可以大展身手了。
function Parent(){
this.name = "parent"
}
Parent.prototype.show = function(){
console.log("hhh")
}
function Child(){
}
//浅拷贝
Child.prototype = Parent.prototype
var p = new Parent();
p.show(); //hhh
console.log(p.name); //parent
var c = new Child();
c.show(); //hhh
console.log(c.name); //undefined
以上通过浅拷贝将Parent原型身上的方法继承了过来,但是如果我们修改了Child原型的show方法,Parent原型身上的方法也会被修改。
//浅拷贝
Child.prototype = Parent.prototype
Child.prototype.show = function(){
console.log("ggg")
}
var p = new Parent();
p.show(); //ggg
console.log(p.name); //parent
var c = new Child();
c.show(); //ggg
console.log(c.name); //undefined
这时候浅拷贝的缺点也就暴露了出来,那么我们使用深拷贝来做。
//深拷贝
for(var i in Parent.prototype){
Child.prototype[i] = Parent.prototype[i];
}
Child.prototype.abc = function(){
console.log("hello")
}
Child.prototype.show = function(){
console.log("ggg")
}
var p = new Parent();
p.show(); //hhh
p.abc(); //报错
console.log(p.name) //parent
var c = new Child();
c.show(); //ggg
c.abc(); //hello
console.log(c.name) //undefined
这样做就可以避免浅拷贝带来的负面影响。但是我们可以看到,不论是浅拷贝还是深拷贝都只继承了原型身上的方法,而没有构造函数中的属性。
那么我们继续往下看。
原型继承 — 原型链继承
function Parent(){
this.name = "hello world"
}
Parent.prototype.show = function(){
console.log(this.name)
}
function Child(){
this.name = "html";
}
Child.prototype = new Parent()
var p = new Parent()
p.show(); //hello world
var c = new Child();
c.show(); //html
以上如果想在Child的实例c中使用一个属性或方法,那么他的查找流程为:
Child的实例c —> c.__ proto __ —> Child.prototype —> Parent的实例p —> p.__ proto __ —> Parent.prototype
逐级向上查找,找到后就停止查找(就近原则)
可以看到在原型链继承中,我们不仅可以继承原型身上的方法和属性,而且还可以继承构造函数中的方法和属性
这个方法其实也又一些缺点,比如 不方便传参
function Parent(n){
this.name = n
}
Parent.prototype.show = function(){
console.log(this.name)
}
function Child(){
}
Child.prototype = new Parent("CSDN")
var p = new Parent("张三")
p.show(); //张三
console.log(p.name) //CSDN
var c = new Child("李四");
c.show(); //张三
console.log(c.name); //CSDN
总结:
原型链继承的特点:
1.更加的简单,方便,易操作
2.不仅可以继承原型身上的方法和属性,而且还可以继承构造函数中的方法和属性
3.但是,不方便传参