1、原型对象
1.1构造函数的缺点
同一个构造函数的多个实例之间,无法共享属性,从而造成对系统资源的浪费。
1.2prototype属性的作用
原型对象的作用,就是定义所有实例对象共享的属性和方法。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。
1.3原型链
JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……
如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype
,即Object
构造函数的prototype
属性。
Object.prototype
的原型是null
。原型链的尽头就是null。
1.4constructor属性
prototype
对象有一个constructor
属性,默认指向prototype
对象所在的构造函数。
由于constructor
属性定义在prototype
对象上面,意味着可以被所有实例对象继承。
constructor
属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。
另一方面,有了constructor
属性,就可以从一个实例对象新建另一个实例。
constructor
属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,一般会同时修改constructor
属性,防止引用的时候出错。
2、instanceof 运算符
instanceof
运算符返回一个布尔值,表示对象是否为某个构造函数的实例。
下面两种写法是等价的:
v instanceof Vehicle
// 等同于
Vehicle.prototype.isPrototypeOf(v)
3、构造函数的继承
第一步是在子类的构造函数中,调用父类的构造函数。
function Sub(value) {
Super.call(this);
this.prop = value;
}
第二步,是让子类的原型指向父类的原型,这样子类就可以继承父类原型。
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.method = '...';
eg:
// 第一步,子类继承父类的实例
function Rectangle() {
Shape.call(this); // 调用父类构造函数
}
// 另一种写法
function Rectangle() {
this.base = Shape;
this.base();
}
// 第二步,子类继承父类的原型
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
4、多重继承
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。
function M1() {
this.hello = 'hello';
}
function M2() {
this.world = 'world';
}
function S() {
M1.call(this);
M2.call(this);
}
// 继承 M1
S.prototype = Object.create(M1.prototype);
// 继承链上加入 M2
Object.assign(S.prototype, M2.prototype);
// 指定构造函数
S.prototype.constructor = S;
var s = new S();
s.hello // 'hello'
s.world // 'world'
上面代码中,子类S
同时继承了父类M1
和M2
。这种模式又称为 Mixin(混入)。
5、模块
JavaScript 不是一种模块化编程语言,ES6 才开始支持“类”和“模块”。
5.1基本实现方法
模块是实现特定功能的一组属性和方法的封装。
简单的做法是把模块写成一个对象,所有的模块成员都放到这个对象里面。
var module1 = new Object({
_count : 0,
m1 : function (){
//...
},
m2 : function (){
//...
}
});
5.2封装私有变量:构造函数的写法
5.3封装私有变量:立即执行函数的写法
5.4模块的放大模式
5.5输入全局变量