面向对象编程
一段完整的创建类的示例
// 如何创建一个类,包含有:静态属性方法,公有私有属性方法?
var Book = (function(){
// 静态私有变量
var bookNum = 0;
// 静态私有方法
function checkName(name){}
var _book = function(id, newName, newPrice){
// 安全模式,防止未使用new操作符
if(this instanceof _book){
// 私有变量 方法
var name, price; function checkID(){}
// 共有属性,方法
this.id = id;
this.getName = function(){ return name; };
this.getPrice = function(){ return price; };
this.setName = function(name){ name = name; };
this.setPrice = function(price){ price = price; };
console.log(++bookNum);
this.setName(newName);
this.setPrice(newPrice);
}
else{
return new _book(id, name, price);
}
};
_book.prototype = {
// 静态公有属性
isJSBook: false,
// 静态共有方法
displayName: function(){
console.log(this.getName());
}
};
return _book;
}());
使用关闭包定义静态私有变量与方法,所有实例创建过程中都能访问到
创建实例时,打印出累加之后的bookNum
当替换Book(即_book)的原型之后,之前创建的实例指向的原型并不会改变,之后在之后创建Book实例时运用新的原型
观察b3、b4的原型
当在原来的原型上变更之后,通过该原型创建的实例都能应用变更之后的原型
这里定义重复了,原型上的getPrice方法将会被实例自身拥有的getPrice方法掩盖住
继承
- 方式一:类式继承: 通过将子类的原型指定为父类的实例
function SuperClass(){
this.superValue = true;
}
SuperClass.prototype.getSuperValue = function(){
return this.superValue;
};
function SubClass(){
this.subValue = true;
}
SubClass.prototype = new SuperClass();
SubClass.prototype.getSubValue = function(){ return this.subValue;};
缺点:
1、如果父类中存在引用类型属性,则会被所有子类共用,一个子类修改之后将会影响到所有子类
2、无法在实例化时为父类传递初始化参数
- 方式二: 构造函数继承: 在子类构造函数中通过call或者apply调用父类的构造函数,实现继承父类定义的属性方法
function SuperClass(id){
this.books = ['js', 'css', 'html'];
this.id = id;
}
SuperClass.prototype.showBooks = function(){
console.log(this.books.join());
};
function SubClass(id){
SuperClass.call(this, id);
}
缺点:
1、子类没有继承父类的原型
2、每个实例化出来的子类对象都会单独拥有一份父类上定义的属性方法,无法复用父类的属性或者方法
- 方式三: 组合继承: 方式一与方式二的组合模式
function SuperClass(name){
this.name = name;
this.books = ['js', 'css','html'];
}
SuperClass.prototype.getName = function(){
console.log(this.name);
};
function SubClass(name, time){
SuperClass.call(this, name);
this.time = time;
}
SubClass.prototype = new SuperClass();
SubClass.prototype.getTime = function(){ console.log(this.time);}
缺点:
1、父类构造函数执行了两遍
2、子类实例化出来的对象原型中重复了一次父类构造函数中的属性方法
- 方式四: 原型式继承: 使用过渡函数作为构造函数,传入的对象作为过渡函数的原型
function inheritObject(o){
function F(){}
F.prototype = o;
return new F();
}
缺点:
跟方式一有相同的缺点
- 方式五: 寄生式继承: 在方式四的基础上进行二次封
function createBook(obj){
var o = inheritObject(obj);
o.getName = function(){
console.log(this.name);
};
return o;
}
- 方式六: 寄生组合式继承: 寄生式继承跟够构造函数继承的组合
function inheritPrototype(SubClass, SuperClass){
var p = inheritObject(SuperClass.prototype);
p.constructor = SubClass;
SubClass.prototype = p;
}
function SuperClass(name){
this.name = name;
this.books = ['js', 'css','html'];
}
SuperClass.prototype.getName = function(){
console.log(this.name);
};
function SubClass(name, time){
SuperClass.call(this, name);
this.time = time;
}
inheritPrototype(SubClass, SuperClass);
SubClass.prototype.getTime = function(){ console.log(this.time);
}
子类添加原型方法只能在现有原型上一个添加
整个过程:先定义子类及父类与父类原型,然后子类原型引用父类的原型对象,然后子类在添加自己的原型方法,最后调用父类构造函数