//第一种构造法:new Object
//通过实例化一个Object来生成对象,构造基本对象直接添加属性
var a = new Object();
a.x = 1,a.y = 2;
//第二种构造法:对象直接量
var b = {x : 1, y : 2};
//第三种构造法:定义类型
function Point(x,y)
{
this.x = x;
this.y = y;
}
var p = new Point(1,2);
var a1 = new Object();
var a2 = new Object();
a1.x = 1,a1.y = 2;
a2.x = 3,a2.y = 4, a2.z = 5;
//你既没有办法说明a1、a2是同一种类型,也没有办法说明它们是不同的类型
</script>
封装:属性或方法可以被声明为公有或者私有,只有共有的属性或方法才可以被外部环境感知和访问
function List()
{
var m_elements = []; //私有成员,在对象外无法访问
m_elements = Array.apply(m_elements,arguments);
//公有属性,可以通过“.”运算符或下标来访问
this.length = {
valueOf : function(){
return m_elements.length;
},
toString : function(){
return m_elements.length;
}
}
this.toString = function(){
return m_elements.toString();
}
this.add = function(){
m_elements.push.apply(m_elements,arguments);
}
}
var alist = new List(1,2,3);
alert("alist: " + alist);
alert("alist.length: " + alist.length);
alist.add(4,5,6);
alert("alist: " + alist);
alert("alist.lenght: " + alist.length);
</script>
使用toString和valueOf模拟getter方法.html
//使用getName()方式
function Foo(a,b)
{
this.a = a;
this.b = b;
this.getSum = function()
{
return a + b;
}
}
alert((new Foo(1,2)).getSum()); //得到3
//模拟getter
function Foo1(a,b)
{
this.a = a;
this.b = b;
this.sum = {
valueOf:function(){return a+b},
toString:function(){return a+b}
}
}
alert((new Foo1(1,2)).sum); //同样得到3
</script>
对象的setter是另一个相对应的属性,它的作用是通过类似赋值的方式改变对象的某些参数或者状态。要实现setter的效果,只有通过定义set<Name>方法来实现。
JavaScript里,对象的属性和方法支持4种不同的类型,第一种类型是私有类型,它的特点是对外界完全不具备被访问性,要访问它们,只有通过特定的getter和setter。第二种类型是动态的公有类型,它的特点是外界可以访问,而且每个对象实例持有一个副本,它们之间不会相互影响。第三种类型是静态的公有类型,或者通常叫做原型属性,它的特点是每个对象实例共享唯一副本,对它的改写会相互影响。第四种类型是类属性,它的特点是作为类型的属性而不是对象类型的属性,在没有构造对象时也能够访问。
<head>
<meta http-equiv="Content-Type" content="text/html;charseet=utf-8">
</head>
<body>
<h1 id="output"></h1/>
<script language="javascript" type="text/javascript">
<!--
function dwn(s){
document.write(s + "<br/>");
}
function myClass(){
var p = 100; //private property;私有属性
this.x = 10; //dynamic public property 动态公有属性
}
myClass.prototype.y = 20; //static public property or prototype property原型属性
myClass.z = 30; //static property //静态属性
var a = new myClass();
dwn("私有属性: " + a.p); //undefined 私有属性对象无法访问到
dwn("动态公有属性: " + a.x); //10 公有属性
dwn("静态公有属性: " + a.y); //20 静态公有属性
a.x = 20;
a.y = 40;
dwn("动态公有属性可以通过对象来改变:" + a.x); //20
dwn("动态公有属性y覆盖了原型属性y:" + a.y); //40
delete(a.x);
delete(a.y);
dwn("删除动态公有属性之后: " + a.x); //undefined 动态公有属性x被删除后不存在
dwn("动态公有属性y被删除后还原为原型属性y :" + a.y) //20
dwn("类属性无法通过对象访问:" + a.z); //undefined
dwn(myClass.z); //30 类属性应该通过类访问
-->
</script>
</body>
</html>
<head>
<meta http-equiv="Content-Type" content="text/html;charseet=utf-8">
</head>
<body>
<h1 id="output"></h1/>
<script language="javascript" type="text/javascript">
<!--
function dwn(s){
document.write(s + "<br/>");
}
function myClass(){
var p = function(s){ dwn(s);}; //private method;私有方法
this.x = dwn; //dynamic public method; 动态公有方法
this.w = function(){ p("私有方法"); };
}
myClass.prototype.y = dwn; //static public method or prototype method原型方法
myClass.z = dwn; //static method //静态方法
var a = new myClass();
//dwn("私有方法: " + a.p('1')); //undefined 私有属性对象无法访问到
a.x('动态公有方法'); //10 公有方法
a.y('静态公有方法'); //20 静态公有方法
a.x = function(s){dwn("修改之后:" + s);};
a.y = function(s){dwn("修改之后:" + s);};
a.x("动态公有方法可以通过对象来改变");
a.y("动态公有方法y覆盖了原型方法y");
delete(a.x);
delete(a.y);
//a.x("删除动态公有方法之后");
a.y("动态公有方法y被删除后还原为原型方法y ")
//a.z("类方法无法通过对象访问:");
myClass.z("类方法");
a.w();
-->
</script>
</body>
</html>
- <html>
- <head>
- <title>Example-21.14 构造继承法</title>
- </head>
- <body>
- <script>
- <!--
- function dwn(s){
- document.write(s + "<br/>");
- }
- //定义一个Collection类型
- function Collection(size){
- this.size = function(){ return size; }; //公有方法,可以被继承 返回的是一个外部环境的参数
- }
- Collection.prototype.isEmpty = function(){ //静态方法,不能被继承
- return this.size == 0;
- }
- //定义一个ArrayList类型,它“继承”Collection类型
- function ArrayList(){
- var m_elements = []; //私有成员,不能被继承
- m_elements = Array.apply(m_elements,arguments);
- //ArrayList类型继承Collection
- this.base = Collection;
- this.base.call(this,m_elements.length);
- this.add = function(){
- return m_elements.push.apply(m_elements,arguments);
- }
- this.toArray = function(){
- return m_elements;
- }
- }
- ArrayList.prototype.toString = function(){
- return this.toArray().toString();
- }
- //定义一个SortedList类型,它继承ArrayList类型
- function SortedList(){
- //SortedList类型继承ArrayList
- this.base = ArrayList;
- this.base.apply(this,arguments);
- this.sort = function(){
- var arr = this.toArray();
- arr.sort.apply(arr,arguments);
- }
- }
- //构造一个ArrayList
- var a = new ArrayList(1,2,3);
- dwn(a);
- dwn(a.size()); //a从Collection继承了size()方法
- dwn(a.isEmpty); //但是a没有继承到isEmpty方法
- //构造一个SortedList
- var b = new SortedList(3,1,2);
- b.add(4,0); //b 从ArrayList继承了add()方法
- dwn(b.toArray()); //b 从ArrayList继承了toArray()方法
- b.sort(); //b 自己实现的sort()方法
- dwn(b.toArray());
- dwn(b); //b没有继承ArrayList中重写的toString()方法
- dwn(b.size()); //b 从Collection继承了size()方法
- //构造继承法不能满足子类和父类都是子类实例的“类型”
- dwn("构造继承法不能满足子类和父类都是子类实例的'类型':" + (a instanceof Collection));
- dwn("构造继承法不能满足子类和父类都是子类实例的'类型':" + (b instanceof ArrayList));
- -->
- </script>
- </body>
- </html>
3
undefined
3,1,2,4,0
0,1,2,3,4
[object Object]
3
构造继承法不能满足子类和父类都是子类实例的'类型':false
构造继承法不能满足子类和父类都是子类实例的'类型':false
- <html>
- <head>
- <title>Example-21.14 构造继承法</title>
- </head>
- <body>
- <script>
- <!--
- function dwn(s){
- document.write(s + "<br/>");
- }
- //定义一个Collection类型
- function Collection(size){
- this.size = function(){ return size; }; //公有方法,可以被继承 返回的是一个外部环境的参数
- }
- Collection.prototype.isEmpty = function(){ //静态方法,不能被继承
- return this.size == 0;
- }
- //定义一个ArrayList类型,它“继承”Collection类型
- function ArrayList(){
- var m_elements = []; //私有成员,不能被继承
- m_elements = Array.apply(m_elements,arguments);
- //ArrayList类型继承Collection
- this.base = Collection;
- this.base.call(this,m_elements.length);
- this.add = function(){
- return m_elements.push.apply(m_elements,arguments);
- }
- this.toArray = function(){
- return m_elements;
- }
- this.size = function(){ return m_elements.length; }
- }
- ArrayList.prototype.toString = function(){
- return this.toArray().toString();
- }
- //定义一个SortedList类型,它继承ArrayList类型
- function SortedList(){
- //SortedList类型继承ArrayList
- this.base = ArrayList;
- this.base.apply(this,arguments);
- this.sort = function(){
- var arr = this.toArray();
- arr.sort.apply(arr,arguments);
- }
- }
- //构造一个ArrayList
- var a = new ArrayList(1,2,3);
- dwn(a);
- dwn(a.size()); //a从Collection继承了size()方法
- dwn(a.isEmpty); //但是a没有继承到isEmpty方法
- //构造一个SortedList
- var b = new SortedList(3,1,2);
- b.add(4,0); //b 从ArrayList继承了add()方法
- dwn(b.toArray()); //b 从ArrayList继承了toArray()方法
- b.sort(); //b 自己实现的sort()方法
- dwn(b.toArray());
- dwn(b); //b没有继承ArrayList中重写的toString()方法
- dwn(b.size()); //b 从Collection继承了size()方法
- //构造继承法不能满足子类和父类都是子类实例的“类型”
- dwn("构造继承法不能满足子类和父类都是子类实例的'类型':" + (a instanceof Collection));
- dwn("构造继承法不能满足子类和父类都是子类实例的'类型':" + (b instanceof ArrayList));
- -->
- </script>
- </body>
- </html>
- this.size = function(){ return m_elements.length; }
3
undefined
3,1,2,4,0
0,1,2,3,4
[object Object]
5
构造继承法不能满足子类和父类都是子类实例的'类型':false
构造继承法不能满足子类和父类都是子类实例的'类型':false
例21.15 原型继承法.html
- <html>
- <head>
- <title>Example-21.15 原型继承法</title>
- </head>
- <body>
- <script>
- <!--
- function dwn(s){
- document.write(s + "<br/>");
- }
- //定义一个Point类型
- function Point(dimension){
- this.dimension = dimension;
- }
- //定义一个Point2D类型
- function Point2D(x,y){
- this.x = x;
- this.y = y;
- }
- Point2D.prototype.distance = function(){
- return Math.sqrt(this.x * this.x + this.y * this.y);
- }
- Point2D.prototype = new Point(2); //Point2D继承了Point
- //定义一个Point3D类型,也继承Point类型
- function Point3D(x,y,z){
- this.x = x;
- this.y = y;
- this.z = z;
- }
- Point3D.prototype = new Point(3); //Point3D也继承了Point
- //构造一个Point2D对象
- var p1 = new Point2D(0,0);
- //构造一个Point3D对象
- var p2 = new Point3D(0,1,2);
- dwn(p1.dimension);
- dwn(p2.dimension);
- dwn(p1 instanceof Point2D); //p1是一个Point2D
- dwn(p1 instanceof Point); //p1 也是一个Point
- dwn(p2 instanceof Point); //p2是一个Point
- -->
- </script>
- </body>
- </html>
运行结果:
2
3
true
true
true
类型的原型可以构成一个原型链,这样就能实现多个层次的继承,继承链上的每一个对象都是实例的类型。
例21.16 prototype的多重继承.html
- <script>
- function dwn(s){
- document.write(s + "<br/>");
- }
- function Point(){
- this.pointName = "Point";
- }
- //Point继承Object,这个通常可以省略,因为自定义类型的缺省原型为Object
- Point.prototype = new Object();
- function Point2D(){
- this.point2DName = "Point2D";
- }
- //Point2D继承Point
- Point2D.prototype = new Point();
- function ColorPoint2D(){
- this.colorPoint2DName = "ColorPoint2D";
- }
- //ColorPoint2D又继承Point2D
- ColorPoint2D.prototype = new Point2D();
- var colorPoint2D = new ColorPoint2D();
- dwn("colorPoint2DName:" + colorPoint2D.colorPoint2DName);
- dwn("point2DName:" + colorPoint2D.point2DName);
- dwn("pointName:" + colorPoint2D.pointName);
- </script>
运行结果:
colorPoint2DName:ColorPoint2D
point2DName:Point2D
pointName:Point
同构造继承法相比,原型继承法的优点是结构更加简单,而且不需要每次构造都调用父类的构造函数(尽管你仍然可以调用它),并且不需要通过复制属性的方式就能快速实现继承。但是它的缺点也是很明显的,首先它不方便直接支持多重继承,因为一个类型只能有一个原型;其次它不能很好地支持多参数和动态参数的父类构造,因为在原型继承的阶段你还不能决定以什么参数来实例化父类对象;第三是你被迫要在原型声明阶段实例化一个父类对象作为当前类型的原型,有的时候父类对象是不能也不应该随便实例化的;最后一个缺点是之前提到过的prototype的"副作用"。
同类继承相比,原型继承本来就是一个简化了的版本,因此我们不应该要求它完全达到标准的类继承的效果,实际上,当你的父类是一个简单、抽象的模型或者是一个接口的时候,原型继承的表现在已知的JavaScript对象继承中是最好的,甚至可以说,prototype继承才是JavaScript文法上提供的真正意义上的继承机制。所以,我们在使用JavaScript时,能够采用原型继承的地方,应当尽可能地采用这种继承方式。
面向对象不是只有类模型一种,prototype-based(基于原型)是class-based(基于类)的简化版本,是一种class-less的面向对象。对应地,prototype继承是class继承的简化版本,相对于class继承来说它简化了许多东西,例如省略了多重继承、基类构造函数、忽略了引用属性的继承......但不能因为它不支持这些特性,就不承认它是一种完整的继承,否则我们就在用class-based的眼光来看待prototype-based,实际上这可能是错误的。
其实prototype-based本来就是class-based的简化版,因此给继承加一个限制,要求父类必须是一个抽象类或者接口,那么prototype-based就没有任何问题了。当然了,也许这么做会使OOP的reuse(重用)能力减弱(以class-based的眼光来看),但是这可以通过其他机制来弥补,比如结合其他类型的继承方式,再比如闭包。
是否为继承添加额外的特性,开发者可以自由选择,但是在不需要这些额外特性的时候,还是有理由尽量用prototype-based继承。
总而言之,prototype-based认为语言本身可能不需要过分多的reuse能力,它牺牲了一些特性来保持语言的简洁,这没有错,prototype-based虽然比class-based简单,但它依然是真正意义上的object-oriented。
例21.17构造继承的局限性.html
- <html>
- <head>
- <title>例21.17构造继承的局限性</title>
- </head>
- <body>
- <script>
- <!--
- function MyDate(){
- this.base = Date;
- this.base.apply(this,arguments);
- }
- var date = new MyDate();
- alert(date.toGMTString);
- //核心对象的某些方法不能被构造继承,原因是核心对象并不像我们自定义的一般对象那样
- //在构造函数里进行赋值或初始化操作
- -->
- </script>
- </body>
- </html>
例21.18 原型继承的局限性.html
- <html>
- <head>
- <title>例21.18 原型继承的局限性</title>
- </head>
- <body>
- <script>
- <!--
- function MyDate(){}
- MyDate.prototype = new Date();
- var date = new MyDate();
- alert(date.toGMTString);
- date.toGMTString();//Date.prototype.toUTCString called on incompatible Object
- -->
- </script>
- </body>
- </html>
我的想法:核心对象Date根本就是用C语言或者其他语言实现的,js本身的继承怎么能够很好的继承呢?
构造函数通常没有返回值,它们只是初始化由this值传递进来的对象,并且什么也不返回。如果函数有返回值,被返回的对象就成了new表达式的值。
111