在JS中创建对象往往使用new xxx()的方法,其中xxx被称为构造函数,实际过程是由new操作符创建一个空对象,然后由后面的xxx方法对该对象进行初始化。在xxx方法中,可以使用this关键字引用到。通过定义构造方法,就定义了一个类。在这种语境下,构造方法的命名通常不同于一般的方法命名(动宾)而用类命名的方式。构造函数通常没有返回值,如果有返回值,返回值将会代替之前用new生成的对象。
如果构造函数返回元数据,对前面初始化的对象没有影响,被赋值的变量还是可以引用到新生成的对象。只有在返回对象时,才会代替返回对象。
function Shape(){this.x = 100;this.y = 100;return new String("this is not the object you are expecting");
}function Shape1(){this.x = 100;this.y = 100;return 3;
}function test(){var temp = new Shape();alert(temp.x);//return undefined.var temp1 = new Shape1();alert(temp1.x);//return 100
}
当函数被当作方法使用时,函数内的this关键字引用的是作为方法所有者的对象。
function Rectangle(w, h){this.width = w;this.height = h;this.area = function(){return this.width * this.height;}
}
使用普通属性作为对象方法的效率是很低的。
每一个JS对象都包括一个内部引用指向另一个对象,成为prototype对象。
如果一个对象是某个对象的PROTOTYPE,那么所有这个对象的属性都将会被把它作为prototype的对象继承
被构造方法初始化的对象的prototype就是构造方法的prototype对象
初始的prototype对象只有一个属性--constructor,指回这个prototype关联的构造器方法
任何prototype对象的属性将被作为该构造器生成的对象的属性(这就是为什么所有对象都有一个constructor属性的原因)
prototype的这种特性使得它成为设置方法,和共有常数的理想位置
另外一个用途是,可以通过prototype生成一个子类:
function heir(p){function f(){};f.prototype = p;return new f();
}
这种继承关系是在查找一个属性时自动发生的,这些被继承的属性并不是在创建新对象时复制到新对象中的。这种设计节约了很多内存空间,另一个特性是,即使在一个继承对象创建之后,对原prototype对象新增的方法见依然可以在被创建对象中得到体现。
继承的属性和普通属性一样,都可以用for/in语句遍历到,只有通过hasOwnProperty()方法才能够识别是不是prototype的属性。
function Rectangle(w,h){this.width = w;this.height = h;
}Rectangle.prototype.area = function(){return this.width*this.height;};....
var temp = new Rectangle(100,100);
alert(temp.hasOwnProperty("area"));//false;
alert("area" in temp);//true;
当读取某个对象的某个属性时,JS先检查该对象是否有该属性,再检查该对象的prototype对象是否有该属性。
当写入某个对象的某个属性时,JS将不使用prototype属性。
这就是说,继承只有在读取对象属性时才发生
如果一个对象o的Prototype有一个属性k,那么o.k =4;将会先在o中寻找是否有一个属性叫k,如果没有就创建一个,并不会到prototype中去寻找。
一般的built-in对象比如String,Date,都可以修改其prototype,但是有些对象,比如浏览器相关的对象,就不具备这种能力。
修改built-in对象的一大作用就是,对某些浏览器没有实现的方法和功能,可以通过修改prototype对象来自己实现。
在JS中模拟类
每一个在constructor中创建并初始化的属性都是instance property.
创建一个instance method,就是通过设置constructor的prototype实现的。
和JAVA重要的不同是,在JAVA类中,this关键字是可以隐藏的,而JS中,必须显式的使用this关键字
定义一个属于constructor的属性来模拟class property.
function Shape(){this.x = 100;this.y = 100;
}Shape.UNIT = 200;
....var temp = new Shape();
alert(temp.UNIT);//returns undefined.
alert(Shape.UNIT);//returns 200;
同样也可以定义一个属于constructor的方法来模拟class method.
以下是完整的定义了一个类Circle
function Circle(radius){this.r= radius;//instance property;
}Circle.PI = 3.14;//class property;Circle.prototype.area = function() { return Circle.PI * this.r * this.r;}
//instance method;//class method
Circle.max = function(a,b){if ( a.r > b.r ) return a;else return b;
}
private member
要实现封装,就需要用到闭包,但要这样做,访问方法只能存在每个对象实例中,不能从Prototype对象继承
function ImmutableRectangle(w,h){this.getWidth = function() {return w;}this.getHeight = function() {return h;}
}ImmutableRectangle.prototype.area = function(){return this.getWidth() * this.getHeight();
};
通用对象方法:
- toString()
Circle.prototype.toString = function(){.....} - valueOf()
在需要将该对象转换成元数据类型时调用。 - 比较方法(== < >。。。)
JS中的比较符比较的是引用而非内容,所以有必要实现自己的equals()方法。如果试图使用<.>这种符号,js将试图调用该对象的valueOf()方法,将对象转换成元对象再比较。
父类和子类:
JS中Object是所有类的父类
对一个对象属性的搜索路径是,先搜索该对象本身,再搜索该对象构造器的prototype,再搜索object的prototype。
继承Rectangle类:
- 定义一个子类构造器PositionedRectangle
function PositionedRectangle(w,h,x,y){Rectangle.call(this,w,h);//调用父类构造器this.x = x;this.y = y; }
- 继承父类prototype
function heir(p){function f(){}'f.prototype = p;return new f(); }PositionedRectangle.prototype = heir(Rectangle.prototype);
调用父类被覆盖的方法:
Rectangle.prototype.toString.call(this);
之所以用call,是因为无法确定toString指向的对象。
JS扩展的另一种方式(不使用继承)
直接把属性和方法拷贝到新的类
可以直接通过拷贝/borrow的方式实现多重继承:
function Colored(c){ this.color = c;}
Colored.prototype.getColor = function(){ return this.color;}function ColoredRectangle(x,y,w,h,c){Rectangle.call(this,x,y,w,h);Colored.call(this,c);
}ColoredRectangle.prototype = heir(Rectangle.prototype);
ColoredRectangle.prototype.constructor = ColoredRectangle;
borrowMethods(Colored, ColoredRectangle);
注意这里prototype.constructor需要重新设置,否则将是父类的构造器.
typeof null是object
typeof undefined是undefined.
typeof function是function
用instanceof来判断到底是哪种对象
如果需要测试某个对象属于具体哪个类,可以使用if( d.constructor == xxxx)
对于built-in对象,toString()方法可以反映对象的类型信息
DUCK Typing
if it walks like a duck and quacks like a duck, it's a duck!
如果一个对象拥有一个类的所有方法/属性,那么就说它是那个类的一个实例