prototype原型相当于一个对象的父类,所有实例都会共享原型上的属性和方法。
constructor构造函数相当于一个创建对象的函数,同时也是对象实例的标识符。bigApple,smallApple使用Apple作为构造函数,初始化就会使用Apple方法;同时这两个实例对象也属于Apple类。
// 原型链继承
// fruit水果类
function Fruit() {
this.name = "水果";
this.color = ["green"];
}
Fruit.prototype.sayName = function () {
console.log(this.name);
}
function Apple() { };
// 继承
Apple.prototype = new Fruit(); // 设置对象原型为Fruit
Apple.prototype.constructor = Apple; // 将构造函数改为Apple
var bigApple = new Apple();
var smallApple = new Apple();
bigApple.sayName(); // 水果
console.log(smallApple.name); // 水果
原理:重写子类的原型对象,将父类的方法和属性继承到子类上
具体原型链如下:
对比没有继承前和继承后的结果
1.继承的Fruit实例会被所有Apple实例(bigApple,smallApple)所共享,如果原型上有引用类型(数组,对象),一旦修改,所有Apple实例的原型都会修改
bigApple.color.push("red");
console.log(bigApple.color); // ["green", "red"]
console.log(smallApple.color); // ["green", "red"]
2.创建子类实例(smallApple,bigApple)时,不能向父类传参。也就是说,子类初始化不能定制父类的属性和方法,父类对象在继承时,已经创建。每个子类实例的原型都一样。
在子类构造函数内容调用父内构造函数方法
// 借用构造函数继承
function Fruit(name) {
this.name = name;
this.color = ["green"];
}
Fruit.prototype.sayName = function () {
console.log(this.name);
}
function Apple(name) {
Fruit.call(this,name);
};
const bigApple = new Apple('big');
const smallApple = new Apple('small');
bigApple.color.push("red");
console.log(bigApple.color); // ["green", "red"]
console.log(smallApple.color); // ["green"]
console.log(bigApple.sayName); //undefined
修改Fruit构造函数的this指向,将其指向Apple的实例对象,给Apple实例初始化
原理:改变父类函数的this指向
优点:实例的属性不会共享,子类构造函数可以传递参数给父类
缺点:父类定义的方法不能被子类继承下来
为了解决上述,父类的方法不能被继承下来的缺点
// 借用构造函数继承
function Fruit(name) {
this.name = name;
this.color = ["green"];
}
Fruit.prototype.sayName = function () {
console.log(this.name);
}
function Apple(name) {
Fruit.call(this,name);
};
Apple.prototype = new Fruit();
Apple.prototype.constructor = Apple;
const bigApple = new Apple('big');
const smallApple = new Apple('small');
bigApple.color.push("red");
console.log(bigApple.color); // ["green", "red"]
console.log(smallApple.color); // ["green"]
bigApple.sayName(); //big
只要子类原型对象设置为父类实例时,子类就可以继承父类原型上的方法。
注意:修改子类原型时,必须将原型上构造函数的指向子类对象。否则实例化子类对象时,会用父类的Fruit方法去构造对象;而不是Apple方法。
优点:解决了子类继承父类方法的问题
缺点:调用两次父类构造函数方法(Fruit.call(this,name);
和new Fruit()
)
针对上面连词使用父类构造函数方法改进
可以使用Object.create()
方法替代
function Fruit(name) {
this.name = name;
this.color = ["green"];
}
Fruit.prototype.sayName = function () {
console.log(this.name);
}
function Apple(name) {
Fruit.call(this,name);
};
Apple.prototype = Object.create(Fruit.prototype);
Apple.prototype.constructor = Apple;
const bigApple = new Apple('big');
const smallApple = new Apple('small');
Object.create()
方法,创建一个新对象,将传入的对象Fruit.prototype
作为父类
原理:将子类的原型对象 指向 父类的原型对象
理论上js不允许一个对象继承多个对象
Object.assign(); 可以将参数二对象上的属性和方法 复制给 参数一对象
function Fruit(name) {
this.name = name;
this.color = ["green"];
}
Fruit.prototype.sayName = function () {
console.log(this.name);
}
function Plant(){
this.desc = "植物类"
}
Plant.prototype.sayHi = function () {
console.log("hi");
}
function Apple(name) {
Fruit.call(this,name);
Plant.call(this);
};
Apple.prototype = Object.create(Fruit.prototype);
// 将植物类原型上的属性和方法复制给Apple原型
Object.assign(Apple.prototype,Plant.prototype);
Apple.prototype.constructor = Apple;
const bigApple = new Apple('big');
bigApple.sayName(); // big
bigApple.sayHi(); // hi