function Fun1 () { this.laji = "uzi" } function Fun2 () { this.strong = "faker" } Fun2.prototype = new Fun1() let fun2 = new Fun2 () console.log(fun2 instanceof Fun1) // true console.log(fun2 instanceof Fun2) // true console.log(fun2 instanceof Object) // true
由于原型链的关系,我们可以说fun2是一个对象Object,Fun1或是Fun2中任何一个类型的实例,所以这三个结果都返回了true
2.isPrototype()
这个方法同样,只要是原型链中出现过的原型,该方法就会返回true,用法如下
console.log(Fun1.prototype.isPrototypeOf(fun2)) // true console.log(Fun2.prototype.isPrototypeOf(fun2)) // true console.log(Object.prototype.isPrototypeOf(fun2))// true
六、原型链的问题
什么?原型链还有问题?买了佛冷,why?
原因一: 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享;
原因二:在创建子类型时,不能向超类型的构造函数中传递参数.
七、如何解决原型链问题?
1.借用构造函数,也叫经典继承
基本思想: 在子类型构造函数的内部调用超类型构造函数
函数只是在特定环境中执行的代码的对象,因此通过使用 apply() 和 call() 方法也可以在(将来)新创建的对象上执行构造函数
看例子:
function Father () { this.team = ["letme","mlxg"] } function Son () { Father.call(this) } let son = new Son() son.team.push("uzi") console.log(son.team) // ["letme", "mlxg", "uzi"] let little_son = new Son() console.log(little_son.team) // ["letme", "mlxg"]
我们可以看出,借用构造函数一举解决了原型链的两大问题:
其一, 保证了原型链中引用类型值的独立,不再被所有实例共享;
其二, 子类型创建时也能够向父类型传递参数.
但是还还还有一个问题,如果仅仅借用构造函数,那么将无法避免构造函数模式存在的问题:
方法都在构造函数中定义, 因此函数复用也就不可用了.而且超类型(如Father)中定义的方法,对子类型而言也是不可见的. so,借用构造函数的技术也很少单独使用.
2.组合继承
组合继承, 有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥两者优点的一种继承模式.
基本思想: 使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承.
这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性.
接着看例子:
function Father (team) { this.team = team this.people = ["mlxg","letme"] } Father.prototype.sayTeam = function () { return console.log(this.team) } function Son (team,age) { this.age = age Father.call(this,team) } Son.prototype = new Father() Son.prototype.sayAge = function () { return console.log(this.age) } let son = new Son("faker",8) son.people.push("uzi") console.log(son.people) // ["mlxg", "letme", "uzi"] son.sayAge() //8 son.sayTeam() // faker let little_son = new Son("bang",3) console.log(little_son.people) // ["mlxg", "letme"] little_son.sayAge() // 3 little_son.sayTeam() // bang
我们可以看出,组合继承既保证了引用类型不再被所有实例所共享,也能够让子类型创建时向父类型传参,同时,原型中的方法又能够被复用,可以说是避免了原型链中的两大问题以及借用构造函数的缺陷,因此他也是js中最常用的继承方式,而且
instanceof 和 isPrototypeOf( )也能用于识别基于组合继承创建的对象.
3.原型继承
基本思想: 借助原型可以基于已有的对象创建新对象, 同时还不必因此创建自定义类型
绳么意思?
比如我们在fun()函数内部, 先创建一个临时性的构造函数, 然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例.
function fun(o){ function F(){} F.prototype = o; return new F(); } let obj = {arr:[11,22] fun(obj).arr.push(33) console.log(fun(obj).arr) // [11,22,33]
在这个例子中,可以作为另一个对象基础的是obj对象,于是我们把它传入到fun()函数中,然后该函数就会返回一个新对象. 这个新对象将arr作为原型,因此它的原型中就包含引用类型值属性. 然后我们向该属性中又增加了一个元素,所以我们能够将它打印出来
*在原型继承中, 包含引用类型值的属性始终都会共享相应的值, 就像使用原型模式一样.
4.寄生式继承
基本思想:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象