1、prototype原型链继承
prototype对象包含所有实例对象需要共享的属性和方法,那些不需要共享的属性和方法就放在构造函数里。
实例对象一旦创建,将自动引用prototype对象的属性和方法。
以dog构造函数举例:
function dog(name){
this.name = name;
}
dog.prototype = { species:'狗' };
var dogA = new dog('小黑');
var dogB = new dog('小黄');
alert(dogA.species); // 狗
alert(dogB.species); // 狗
优点:
父类的方法得到了复用。
缺点:
原型中属性的改变会反应到所有的实例上
创建子类的实例时,不能向父类的构造函数传递参数
2、利用父构造函数继承属性
function Father(uname, age){
this.uname = uname;//这里this指向父构造函数
this.age = age
}
function Son(uname, age,score,job){
Father.call(this, uname, age)//调用父构造函数,这里的this一定要写在第一个,而且可以表示多个参数
this.score = score//自己的属性
this.job= job
}
var son = new Son('李丽',18,100,'学生')
console.log(son)//Son {uname: "李丽", age: 18,score:100,job:"学生"}
3、构造函数继承与原型链继承组合
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function() {
console.log(100000);
};
// 2 .子构造函数
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score;
}
// Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;
// 这个是子构造函数专门的方法
Son.prototype.exam = function() {
console.log('孩子要考试');
}
var son = new Son('刘德华', 18, 100);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);
4、ES6继承
class Calculator {
constructor(a,b){
this.a = a;
this.b = b;
}
add(a,b){
return ("相加結果:"+ (a+b))
}
}
class Son extends Calculator{
add(a,b){
console.log(super.add(a,b))
}
}
var rt = new Son()
rt.add(3, 4)
super.add(a,b)就是调用父类中的普通函数
继承中属性和方法的查找原则是就近原则:
class Calculator {
add(){
console.log ("我是父親")
}
}
class Son extends Calculator{
add(){
console.log ("我是兒子")
}
}
var son = new Son()
son.add()//我是兒子
子类扩展函数,并调用父类的构造函数:
class Calculator {
constructor(a,b){//構造函數
this.a = a;
this.b = b;
}
add(a,b){//普通函數
console.log ("相加結果:"+ (a+b))
}
}
class Son extends Calculator{
constructor(a,b,c){
//利用super調用父類構造函數,super必須在this之前,爹是最大的
this.c = c //ReferenceError
super(a,b)
this.c = c // 正确
}
reduce(a,b,c){
console.log ("相減結果:"+ (a-b-c))
}
}
var rt = new Son()
rt.add(3, 4)
rt.reduce(3, 4)
this指向问题,谁调用,this就指向谁:
var _that
class Calculator {
constructor(a,b){//构造函数
_that = this//将constructor的this给that
this.a = a;
this.b = b;
document.querySelector('button').onclick = this.add
}
add(){
//这里的this指向的是button按钮,谁调用,this就指向谁
//我们要的值是存在constructor中,所以用that将constructor弄过来
console.log(this)
console.log ("相加結果:"+ (_that.a+_that.b))
}
}
var rt = new Calculator(3,4)
ES6原型问题总结
//类的 prototype 属性和__proto__属性
class A {
}
class B extends A {
}
B.__proto__ === A // true 子类的__proto__属性,表示构造函数的继承,总是指向父类。
B.prototype.__proto__ === A.prototype // true 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
//实例的 __proto__ 属性
//子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。
var p1 = new A();
var p2 = new B();
p1.__proto__ === A.prototype // true
p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true
总结:
ES5中:
利用借用构造函数实现实例属性和方法的继承 ;
利用原型链或者寄生式继承实现 共享的原型属性和方法的继承 。
ES6中:
利用class定义类,extends实现类的继承;
子类constructor里调用super()(父类构造函数)实现 实例属性和方法的继承;
子类原型继承父类原型,实现原型对象上方法的继承。