基本概念
在java中,类有如下四种成员:类属性,类方法,实例属性,实例方法。
js中没有这些概念,但是js中的类有如下3中成员:
constructor: 构造函数,上面可以定义类属性和类方法.但是这些属性和方法只能通过类来访问,不能通过实例来访问。
prototype: 原型,定义实例方法,被所有实例共享
instance object: 定义实例属性,每个实例都有自己的属性
因此,定义类有三个基本步骤:
1,定义构造函数,构造函数内部定义实例属性。
2,在prototype上定义实例方法
3,在构造函数上定义类属性和类方法,
function People(name){
this.name = name;
}
People.prototype.say = function(m){
console.log(this.name + ":" + m);
}
People.type = "people";
instanceof
instanceof 是通过比较prototype来实现的。
function A(){};
var a = new A();
a instanceof A;//true
var b={};
b instanceof A;//false
b.__proto__ = A;
b instanceof A;//true
利用闭包实现私有属性和方法
私有属性
js不支持private,可以利用闭包来实现私有属性。
function People(name){
this.getName = function(){return name;}; //name是私有变量,只能通过set/get来访问。
this.setName = function(n){name = n;}
}
var p = new People("Bob");
p.name; //undefined
p.getName(); //Bob
p.setName("Lily");
p.getName(); //Lily
不同于java等语言,这样实现私有属性后,3点不足之处:
1,getter/setter 在每个实例中都有一份拷贝,不符合实例共享同一组方法的原则,而且影响性能。
2,即使在类自身的方法中也无法用this.name来访问name,不过这样未必是坏事。
3,get/set必须定义在构造函数中,使构造函数很臃肿。
私有方法
还是利用闭包来实现
var People = (function(){
function People(name){
this.name = name ;
};
People.prototype.sayName = function(){ //public
_sayName.call(this); //call private method
};
function _sayName(){ //private
console.log(this.name);
};
return People;
}());
继承
继承的关键是继承原型和指定构造方法,即如下两行关键代码:
B.prototype = inherit(A.prototype); //从原型继承A的实例方法,inherit的定义在下面
B.prototype.constructor = B; //否则就变成A了
一个继承的完整例子:
function People(name){ this.name = name; } People.prototype.sayName = function(){ alert(this.name); } function Student(name, studentID){ People.call(this, name); this.id = studentID; } Student.prototype = inherit(People.prototype); //关键点1 Student.prototype.constructor = Student; //关键点2 Student.prototype.sayID = function(){ alert(this.id); }
//封装一个完整的方法
//从一个prototype继承一个新的对象,即 newObj.prototype = p; function inherit(p){ if(p == null) throw TypeError(); if(Object.create) return Object.create(p); var t = typeof p; if(t != "object" && t != "function") throw TypeError(); function f(){}; f.prototype = p; return new f(); } //把p的可枚举属性复制到o中,o中同名属性会被覆盖 function extend(o, p){ for(pro in p) o[pro] = p[pro]; return o; } function defineSubClass(superClass //父类 , constructor //构造函数 , methods //实例方法,添加到prototype上 , statics //静态属性,被添加到构造函数上 ){ constructor.prototype = inherit(superClass.prototype); constructor.prototype.constructor = constructor; if(methods) extend(constructor.prototype, methods); if(statics) extend(constructor, statics); return constructor; } Function.prototype.extend = function(constructor, methods, statics){ return defineSubclass(this, constructor, methods, statics); }
接口,抽象类和抽象方法
因为js没有提供对接口、抽象类和抽象方法的语法支持,所以一般的实现方式是:默认抛出异常,因此在实现类中不重写这些方法就会出错。
function abstractMethod = function(){ //一个全局的抽象方法 throe new Error("unimplemented abstract method!"); } function AbstractClassOne = function(){ //抽象类 throe new Error("can't instantiate abstract Class!"); } AbstractClassOne.prototype.prop1 = abstractMethod; //抽象方法