1. 原型继承
利用原型(链)让一个对象继承另一个对象的属性和方法
<script type="text/javascript">
//原型对象的属性和方法,能被实例所访问到
//每一个构造函数都有一个原型对象
//每一个原型对象都有一个指针 constructor 指向构造函数
//每一个实例都有一个内部指针(_proto_) 指向原型对象,
function Person(name,age){
}
Person.prototype.name = "john";
Person.prototype.age = 20;
Person.prototype.sayHello = function(){
console.log(this.name,this.age);
}
function Male(){
}
Male.prototype = new Person();//内部__proto__指针 Male.prototype.__proto__ ->(指向) Person.prototype
var male = new Male();
//这种链式的结构叫做 原型链
//原型链上的属性和方法能够被实例所访问到
//函数都有prototype, 实例有__proto__
//将其他类型转换成字符串时,默认会调用toString方法,这个方法是顶层原型对象上的方法,可以改写
//改写之后,转换的结果以改写结果为准
//console.log(male.__proto__ == Male.prototype);//__proto__ 指向父级的 prototype 最终指向Object.prototype, --证明了JS是基于原型的, JS万物皆对象
male.sayHello();
</script>
这种继承的缺点是:构造函数原型上的属性在所有该构造函数构造的实例上是共享的,即属性没有私有化,原型上属性的改变会作用到所有的实例上
2. 构造函数基层
A类 B类 B要继承A
让子类具有父类的实例方法
构造函数的继承方式
call和apply继承的方法
<script type="text/javascript">
function Person(name,age){
this.name = name;
this.age = age;
this.sayHello = function(){
onsole.log(this.name,this.age);
}
}
function Male(name,age){
Person.call(this,name,age);//call参数列表
}
//apply
function Female(name,age){
Person.apply(this,[name,age]);//apply参数数组
}
var text1 = new Male("text1",20);
text1.sayHello();
var text2 = new Female("text2",21);
text2.sayHello();
</script>
这种方法继承的缺点是:实现了属性的私有化,但是子类无法访问父类原型上的属性。
3. 组合继承
使用原型链实现对原型属性和方法的继承
通过伪造(冒充)构造函数来实现对实例属性的继承
<script type="text/javascript">
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function(){
console.log(this.name,this.age);
};
function Male(name,age){
Person.call(this,name,age);
}
//Male.prototype = new Person();//这种方法会反复调用Person
//Male.prototype = Person.prototype;//这种方法会扰乱父子类之间的关系,会出现地址拷贝
//子类添加给自己的一些方法,父类不应该访问到
//遍历Person.prototype
for(var i in Person.prototype){
Male.prototype[i] = Person.prototype[i];
}
var male = new Male("john",20);
male.sayHello();
</script>
4. 寄生式继承
在原型式继承得到对象的基础上,在内部再以某种方式来增强对象,然后返回。
<script type="text/javascript">
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.sayHello = function(){
console.log(this.name,this.age);
};
function Male(name,age){
Person.call(this,name,age);
}
//借用Object.create方法
Object.create();
var obj1 = {a:1};
var obj2 = {b:2};
var a = Object.create(obj1);
console.log(a);
Male.prototype = Object.create(Person.prototype);
//需要纠正指向问题
Male.prototype.constructor = Male;
var male = new Male("john",20);
male.sayHello();
</script
5. ES6继承
ES6中有关class的继承方式,引入了extends关键字。但其本质仍然是构造函数+原型链的组合式继承。
<script type="text/javascript">
class Person{
constructor(name,age){
this.name = name;
this.age = age;
}
sayHello(){
console.log(this.name);
}
static foo(){ //它是静态方法,就是这个类本身的方法 , 如果想要访问foo 需要通过类本身调用 Person.foo()
console.log("foo");
}
}
class Male extends Person{
constructor(name,age){
super(name,age); //在构造函数里面它是指向父类的构造函数
this.sexy = "male";
}
sayHi(){
super.sayHello();//指向父类的原型对象
}
static bar(){
super.foo();//指向父类
}
}
console.log(Person.prototype)
console.dir(Person)
Person.foo()
var male = new Male("john",20)
male.sayHello();
male.sayHi();
</script>