原型链
原型链是由__Proto__串联起来的链状结构。
每一个函数都有一个prototype属性,也有__proto__,每一个对象只有一个__proto__属性;一般我们把属性方法放在构造函数中,把方法挂在prototype下面,对象的__proto__指向构造函数的prototype,原型链的终点为Object.prototype,再往上Object.prototype.proto=null
1、实例没有具体的构造函数
let obj = {}
console.log(obj.__proto__== Object.prototype) //true
onsole.log(Object.prototype.__proto__) //null
2、实例的构造函数为自定义的构造函数
function Persion(){}//构造函数
let p1 = new Persion()//实例
console.log(p.__proto__==Persion.prototype) //true
console.log(Persion.prototype.__proto__==Object.prototype) //true
console.log(Object.prototype.__proto__) //null
3、实例的构造函数为内置构造函数
let p = new Array()
console.log(p.__proto__ == Array.prototype) //true
console.log(Array.__proto__ ==Function.prototype)//true
console.log(Function.__proto__ == Function.prototype)//true ,Function 是构造函数,也是对象
console.log(Function.prototype.__proto__==Object.prototype)//true
console.log(Object.prototype.__proto__)//null
1、原型链继承子类的原型指向父类的实例(子类的.prototype=new 父类())
javascript
function Child() {} //子类
// Child.prototype.sleep=function(){
// console.log(this.name+"is sleeping")
// }1
Child.prototype = new Parent("yzh")
//为了不破坏原型链,将Male的constructor指向本身
Child.prototype.constructor=Child
Child.prototype.sleep=function(){
console.log(this.name+" is sleeping")
}//2
Parent.prototype.eat=function(){
console.log(this.name+" is eating")
}
var child=new Child()
console.log(Child)
child.introduce()
child.hobby("唱歌")
child.sleep()
child.eat()
console.log(child instanceof Parent) //true
console.log(child instanceof Child) //true
注:如上如果Male的原型方法sleep放在1位置时,会报错child.sleep is not a function,放在2位置, 输出 yzh is sleeping
此种继承方法的特点:
1、子类与父类的关系为指向关系,实例是子类的实例,也是父类的实例 2、父类新增的原型方法或属性,子类都能访问 3、简单易用 缺点:
1、如果要为子类新增属性或者方法,只能在new Parent() 之后,并不能放在构造函数中,如上的代码 例,如果新增的方法放在改变子类原型的指向之前,改变指向之后新增的方法自然就没用了,子类的prot otype已经指向了父类了
2、子类的所有实例,共用所有的父类属性,子类不能拥有自己的属性,如果有多个实例时,其中一个实修 改了父类引用类型的值,那么所有的实例都会发生改变,例如我只想其中的一个实例的eat方法改为"is eating apple",那么所有的实例该方法都会发生改变
3、不能多继承,因为是改变了原型链的指向,不能指向多个父类,因此只能单继承 4、创建子类时,无法向父类构造函数传参,因为在改变子类的原型链指向之后,子类的属性和方法是无效 的
2、构造继承(call,apply继承)把 父对象的所有属性和方法,拷贝进子对象
javascript
function Child(name){
Parent.call(this,name)
}
var child=new Child("yzh")
console.log(child.name) //yzh
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //false
console.log(child instanceof Child) //true
优点:
1、解决了原型链继承中的子类共享父类属性的问题
2、创建的子类实例可以向父类传递参数
3、可以实现多继承,call改变父类的this
缺点:
1、实例是子类的实例,不是父类的
2、只能继承父类的实例属性和方法,不能继承父类原型上的方法
3、无法实现函数复用,每个子类都有父类函数的属性和方法的副本,当child调用Parent上的方法时,Parent内部的this指向的是child,Parent内部的this上的属性和方法都被复制到了child上面,如果每个子类的实例都复制一遍父类的属性和方法,就会占用很大的内存,而且当父类的方法发生改变了时,已经创建好的子类实例并不能更新方法,因为已经复制了原来的父类方法当成自己的方法了。
3、组合继承(原型链继承与构造继承)
javascript
function Child(name) {
Parent.call(this,name) //构造继承 ,第二次调用父类
}
//原型链继承
Child.prototype=new Parent()
Child.prototype.constructor=Child
var child=new Child("yzh") //子类的实例向父类传递参数,第一次调用
父类
console.log(child.name)
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //true
console.log(child instanceof Child) //true
优点:结合了原型链继承和构造继承的优点
1、子类可向父类传参
2、实例既是子类的实例,也是父类的实例
3、多个实例之间不存在公用父类的引用属性的问题
4、实例可以继承父类实例的属性和方法,也可以继承原型上的属性和方法
缺点:这种方式调用了两次父类的构造函数,生成了两份实例,相同的属性既存在于实例中也存在于原型中
4、拷贝继承
javascript
function Child(name){
var parent = new Parent(name);
for(var key in parent){
Child.prototype[key] = parent[key];
}
}
var child=new Child("yzh")
console.log(child.name) //yzh
child.introduce()
child.hobby("sing")
console.log(child instanceof Parent) //false
console.log(child instanceof Child) //true
优点:
1、 支持多继承
缺点:
1、无法获取父类不可枚举的方法,这种方法是用for in 来遍历Parent中的属性,例如多选框的checked属性,这种就是不可枚举的属性
2、 效率很低,内存占用高
5、寄生组合继承
寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法.
思路:不必为了指定子类的原型而调用父类的构造函数,我们所需要的无非就是父类原型的一个副本而已.本质上,就是使用寄生式继承来继承父类的原型,然后在将结果指定给子类的原型
其实就是在不需要实例化父类构造函数的情况下,也能得到父类的属性和方法,就是直接实例化一个临时的父类的副本,实现原型链继承
javascript
function Child(name) {
Parent.call(this,name) //构造继承
}
(function(){
//创建一个临时的类
var Temp=function(){}
Temp.prototype=Parent.prototype
//子类的原型指向父类的实例
Child.prototype=new Temp()
})()
var p=new Child("yzh")
console.log(p.name)//yzh
p.hobby("sing")//yzh like sing
p.introduce()//my name is yzh
优点:结合组合继承的所有优点,此方法比较完美
6、ES6继承
javascript
class Parent{
constructor(name){
this.name=name
}
introduce(){
console.log("my name is " + this.name)
}
hobby(hobby){
console.log(this.name + " like " + hobby)
}
}
class Child extends Parent{
constructor(name,age){
super(name) //构造继承,可以继承Parent构造函数上的属性
this.age=age
}
}
var p = new Child("yzh")
p.introduce() //my name is yzh
p.hobby("apple")//yzh like apple
console.log(p instanceof Parent) // true
console.log(p instanceof Child) //true
我们用es6的写法让对象的写法更加的清晰,更像面对对象的编程;在这个方法中使用extends 和 super 两个关键字,在子类的constructor方法中调用super方法,用来继承父类的this对象