对象原型链与继承
创建对象的方法
- 工厂模式
- 构造函数模式
- 原型模式
- 混合构造函数和原型模式
- 动态原型模式
- 寄生构造函数模式
- 稳妥构造函数模式
//字面量创建
var o1 = {name: 'value'};
var o2 = new Object({name: 'value'});
//构造函数创建
var M = function() {this.name = 'o3'};
var o3 = new M();
//Object.create()
var P = {name: 'o4'};
var o4 = Object.create(P)
原型
JavaScript 的所有对象中都包含了一个 proto 内部属性,这个属性所对应的就是该对象的原型
JavaScript 的函数对象,除了原型 proto 之外,还预置了 prototype 属性
当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 proto。
原型链
任何一个实例对象通过原型链可以找到它对应的原型对象,原型对象上面的实例和方法都是实例所共享的。
一个对象在查找以一个方法或属性时,他会先在自己的对象上去找,找不到时,他会沿着原型链依次向上查找。
注意: 函数才有prototype,实例对象只有有__proto__, 而函数有的__proto__是因为函数是Function的实例对象
instanceof原理
判断实例对象的__proto__属性与构造函数的prototype是不是用一个引用。如果不是,他会沿着对象的__proto__向上查找的,直到顶端Object。
判断对象是哪个类的直接实例
使用对象.construcor直接可判断
构造函数,new时发生了什么?
var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
创建一个新的对象 obj;
将这个空对象的__proto__成员指向了Base函数对象prototype成员对象
Base函数对象的this指针替换成obj, 相当于执行了Base.call(obj);
如果构造函数显示的返回一个对象,那么则这个实例为这个返回的对象。 否则返回这个新创建的对象
类
类的声明
// 普通写法
function Animal() {
this.name = 'name'
}
// ES6
class Animal2 {
constructor () {
this.name = 'name';
}
}
继承
借用构造函数法
在构造函数中 使用Parent.call(this)的方法继承父类属性。
原理: 将子类的this使用父类的构造函数跑一遍
缺点: Parent原型链上的属性和方法并不会被子类继承
function Parent() {
this.name = 'parent'
}
function Child() {
Parent.call(this);
this.type = 'child'
}
原型链实现继承
原理:把子类的prototype(原型对象)直接设置为父类的实例
缺点:因为子类只进行一次原型更改,所以子类的所有实例保存的是同一个父类的值。
当子类对象上进行值修改时,如果是修改的原始类型的值,那么会在实例上新建这样一个值; 但如果是引用类型的话,他就会去修改子类上唯一一个父类实例里面的这个引用类型,这会影响所有子类实例
function Parent() {
this.name = 'parent'
this.arr = [1,2,3]
}
function Child() {
this.type = 'child'
}
Child.prototype = new Parent();
var c1 = new Child();
var c2 = new Child();
c1.__proto__ === c2.__proto__
组合继承方式
组合构造函数中使用call继承和原型链继承。 原理: 子类构造函数中使用Parent.call(this);的方式可以继承写在父类构造函数中this上绑定的各属性和方法; 使用Child.prototype = new Parent()的方式可以继承挂在在父类原型上的各属性和方法 缺点: 父类构造函数在子类构造函数中执行了一次,在子类绑定原型时又执行了一次
function Parent() {
this.name = 'parent'
this.arr = [1,2,3]
}
function Child() {
Parent.call(this);
this.type = 'child'
}
Child.prototype = new Parent();