原型及原型链
proto 和 prototype 分别是什么
- proto 为 JavaScript 的对象特殊的内置属性,是对于其他对象的引用,就是说谁创建了这个对象,该属性就指向谁。官方写法为:
[[Prototype]],不可直接获取,浏览器实现为:__proto__。 - prototype 当通过构造函数调用方式创建对象实例时,生成的实例对象的
__proto__属性指向(函数.prototype)这个对象,Foo.prototype:这是一个对象
object 再往上是 null
Object.prototype.__proto__ === null; // true
以下情况,__proto__ 和 prototype 指向同一个
Function.__proto__ === Function.prototypearr.__proto__ === Array.prototype;- 其他手动修改的方式
原型链断裂
原型链断裂之后将无法通过原型链获取原型链上的属性、方法
js 原型链继承及实现
js prototype 对象实现原型链及继承
js 的继承其实是委托,实例只是指向父对象,本身并不具有父对象的属性和方法,不像其他语言那样通过类似拷贝的方式创建实例
父类
function Father(name) {
this.name = name;
this.sum = function () {
console.log(this.name);
};
}
Father.prototype.age = 10;
原型链继承
特点
- 实例继承:实例的构造函数的属性、父类构造函数属性、父类原型属性
- 父类新增原型方法、属性,所有子类都能访问
缺点
- 创建实例时无法向父类构造函数传参
- 所有实例都共享父类原型的属性,一旦修改原型属性,则所有实例都会变化
function Son() {
this.name = 'ker';
}
// 主要
Son.prototype = new Father();
var son1 = new Son();
son1.age; // 10
son1 instanceof Father; // true
构造函数继承
特点
- 只继承父类构造函数的属性,没有继承父类原型的属性
- 解决了原型链继承的缺点
- 可以继承多个构造函数属性(call 多个)
- 在子实例中可以向父实例传参
缺点
- 只能继承父类构造函数的属性
- 无法实现构造函数的复用,每个新实例都有父类构造函数的副本
- 实例并不是父类的实例,只是子类的实例
function Son2() {
// 重点
Father.call(this);
this.name = 'Tom';
this.age = 12;
}
var son2 = new Son2();
son2.name; // Tom
son2 instanceof Father; // false
组合继承(组合原型链继承和构造函数继承,常用)
特点
- 可以继承父类原型上的属性,可传参,可复用
- 每个实例引入的构造函数属性是私有的
- 即是子类的实例,也是父类的实例
缺点:调用了两次父类构造函数,生成两份实例,子类构造函数会代替原型上的父类构造实例
function Son3(name) {
// 借用构造函数继承
Father.call(this);
this.name = name;
}
// 原型链继承
// 第一次调用父类构造函数
Son3.prototype = new Father();
// new Son3 的时候,构造函数内部第二次调用父类构造函数
var son3 = new Son3('gar');
son3.name; // gar
son3 instanceof Father; // true
原型式继承(Object.create() 就是这个原理)
特点:类似于复制一个对象,用函数来包装
缺点
- 所有实例都会继承原型上的属性
- 无法实现复用
// 重点,封装一个函数容器,用来输出对象和承载继承的原型
function content(obj) {
function F() {}
F.prototype = obj;
return new F();
}
// 创建父类实例
var son = new Father();
var son4 = content(son);
son4.age; // 10
son4 instanceof Father; // true
寄生式继承(就是给原型式继承在外面套个壳子)
特点:没有创建自定义类型,只是套个壳子返回对象,这个函数就创建了新对象
缺点:没有用到原型,无法复用
function content(obj) {
function F() {}
F.prototype = obj;
return new F();
}
var son = new Father();
// 以上为原型式继承,给原型式继承再套个壳子传递参数
function Son5(obj) {
var sub = content(obj);
sub.name = 'gar';
return sub;
}
var son5 = Son5(son);
son5 instanceof Father; // true
son5 instanceof Son5; // false
寄生组合式继承(常用)
解决了组合式继承两次调用构造函数属性的缺点
// 寄生
function content(obj) {
function F() {}
F.prototype = obj;
return new F();
}
// con 实例的原型继承了父类函数的原型,更像原型链继承,只不过只继承了原型属性
var con = content(Father.prototype);
// 组合
function Son6() {
// 继承了父类构造函数的属性
Father.call(this);
}
// 重点
// 继承了 con 实例
Son6.prototype = con;
// 修复 constructor 指向
con.constructor = Son6;
// Son6 的实例就继承了构造函数属性,父类实例,con 的函数属性
var son6 = new Son6();
son6 instanceof Father; // true
son6 instanceof Son6; // true