article:
tag:
just test

其他 > Javascript - 面向对象编程 上一篇 | 下一篇

translation, oop in js, oop, oo, js, shawl.qiu

Javascript - 面向对象编程

作者:Gavin Kistner 中译:shawl.qiu

 

目录:

一、公有/变量及方法

二、继承

三、附录

 

内容:

一、      公有/私有的变量及方法

本部分使用例子 Person演示如何在 Javascript 类中创建属于类的变量及方法。

部分二阐述Javascript 中类的继承。

摘要
私有变量 使用关键字 ‘var’ 在对象内部定义, 变量可访问范围为对象内部、内部私有函数,特有方法。

私有函数 为对象内部定义的函数, 或使用 var functionName=function(){...} 定义的函数

特有方法 使用 this.methodName=function(){...} 定义,可供对象外及内调用。

公有属性 使用 this.variableName 定义,在对象外部可读/写公有属性。

公有方法 使用 Classname.prototype.methodName = function(){...} 定义,可供外部调用。

公有属性 使用 Classname.prototype.propertyName = someValue 定义。

静态属性 使用 Classname.propertyName = someValue 定义。

 

 

范例
在下面例子中,一个人的名字和种族依赖于创建实例时的设置。

当创建实例时,一个人的年龄是1岁和一个不可见的寿命岁数。

一个人有 weight[体重] 变量,可从“eating[吃东西] 方法和 exercising[运动] 方法中更改.

每执行 eating 一次, weight 增加三倍 weight*=3

每执行 exercising 一次, weight 减少两倍,即 weight/=2

 

每当人进行吃东西和运动时, 他们的年龄增加一岁。

Person 对象有一个所有人可更改的公有属性 clothing[着衣], 和另一个公有属性 ‘dirtFactor[污垢值].

每当人吃东西和运动时,dirtFactor 增加,当执行 shower()[沐浴] 方法时, dirtFactor 减少。

范例源码

<script type="text/javascript">
/*<![CDATA[*/
function Person(n, race)
{
 
this.constructor.population++ ;
 
// ************************************************************************ 
 
// 私有变量及方法 
 
// 只有 特有方法 可访问编辑及执行 
 
// *********************************************************************** 
 
var alive = true, age = 1;
 
var maxAge = 70 + Math.round(Math.random() * 15) + Math.round(Math.random() * 15);
 
 
function makeOlder()
 
{
  
return alive = (++ age <= maxAge)
 
}
 
var myName = n ? n : "John Doe";
 
var weight = 1;
 
// ************************************************************************ 
 
// 特有方法
 
// 可供外部及内部执行
 
// ************************************************************************ 
 
this.toString = this.getName =
  
function()
  
{
   
return myName
  
}
 
 
this.eat =
  
function()
  
{
   
if (makeOlder())
   
{
    
this.dirtFactor++ ;
    
return weight *= 3;
   
}
   
else alert(myName + " can't eat, he's dead!");
  
}
 
 
this.exercise =
  
function()
  
{
   
if (makeOlder())
   
{
    
this.dirtFactor++ ;
    
return weight /= 2;
   
}
   
else alert(myName + " can't exercise, he's dead!");
  
}
 
 
this.weigh =
  
function()
  
{
   
return weight
  
}
 
 
this.getRace =
  
function()
  
{
   
return race
  
}
 
 
this.getAge =
  
function()
  
{
   
return age
  
}
 
 
this.muchTimePasses =
  
function()
  
{
   age 
+= 50;
   
this.dirtFactor = 10;
  
}
 
// ************************************************************************ 
 
// 公有属性  可供任何人读写
 
// ************************************************************************ 
 
this.clothing = "nothing/naked";
 
this.dirtFactor = 0;
}

// ************************************************************************ 
// 公有方法  可供任何人读写
// ************************************************************************ 
Person.
prototype.beCool =
 
function()
 
{
  
this.clothing = "khakis and black shirt"
 
}

Person.
prototype.shower =
 
function()
 
{
  
this.dirtFactor = 2
 
}

Person.
prototype.showLegs =
 
function()
 
{
  alert
(this + " has " + this.legs + " legs")
 
}

Person.
prototype.amputate =
 
function()
 
{
  
this.legs--
 
}
 
// ************************************************************************ 
// 元型属性 -- 可供任何人读写(但不可叠加)
// ************************************************************************ 
Person.
prototype.legs = 2;
// ************************************************************************ 
// 静态属性  可供任何人读写 
// ************************************************************************ 
Person.population 
= 0;

// 这是一个使用 Person 类的例子
function RunGavinsLife()
{
 
var gk = new Person("Gavin""caucasian");
 
//创建一个新的 Person 实例
 
 
var lk = new Person("Lisa""caucasian");
 
//创建一个新的 Person 实例
  
 alert
("There are now " + Person.population + " people");
 
 gk.showLegs
()
 lk.showLegs
()
 
// Gavin  Lisa 共享元型属性 Person.prototype.legs 使用 this.legs 访问
 
 gk.race 
= "hispanic"
 
//写入公有变量 race 但并非覆盖私有变量 race
 
 alert
(gk + "'s real race is " + gk.getRace())
 
//从私有变量 race 返回变量值 caucasian 私有 race 变量在创建实例时设置
 
 gk.eat
();
 gk.eat
();
 gk.eat
();
 
//eat 方法返回 weight(体重), 每次 * 3  3, 9, 27
 
 alert
(gk + " weighs " + gk.weigh() + " pounds and has a dirt factor of " + gk.dirtFactor);
 gk.exercise
();
 
//执行锻炼方法, 每次 weight/=2
 
 gk.beCool
();
 
//赶时髦方法。。。 
 
 gk.clothing 
= "Pimp Outfit";
 
//clothing 是个公有变量,可供外部更新。。。 
 
 gk.shower
();
 alert
("Existing shower technology has gotten " + gk + " to a dirt factor of " + gk.dirtFactor);
 gk.muchTimePasses
();
 
//五十年后 
 
 Person.
prototype.shower =
  
function()
  
{
   
//为所有人重设 shower(沐浴方法,洗掉身上的污垢。。。
   
this.dirtFactor = 0;
  
}
 
 gk.beCool 
=
  
function()
  
{
   
//更新着衣流行
   
this.clothing = "tinfoil";
  
};
 
 gk.beCool
();
 gk.shower
();
 alert
("Fashionable " + gk + " at "
  
+ gk.getAge() + " years old is now wearing "
  
+ gk.clothing + " with dirt factor "
  
+ gk.dirtFactor);
  
 gk.amputate
();
 
//截肢函数。。。
 
 gk.showLegs
();
 lk.showLegs
();
 
//Gavin 被截去一条腿,Lisa 完好无损
  
 gk.muchTimePasses
();
 
// 50 年过去,Gavin 现在超过100
  
 gk.eat
();
 
//Gavin 已百年, 不能再吃东西。。。
}

RunGavinsLife
();
/*]]*/
</script>

 

备注:
maxAge 是一个私有变量,且没提供特有方法的访问,因此外部无法访问。

race
是一个私有变量,只从对象参数进行定义。
变量从对象构造器传递可视为私有变量。

值为 “tinfoil” 的方法“beCool()” 仅供 gk 对象使用,主要为 Gavin老年时的着衣选择, 并非提供给 Person 类。
其他 Person 实例依旧使用旧的 “beCool()” 方法,也就是着衣元素为 “khakis black shirt”

注意隐式调用,即 gk.toString() 方法,它使对象实例可以像字串那样拼加。
也就是说, alert(gk+' is so cool.') 时, gk 输出为 Gavin, alert(gk.toString()+' is so cool.') 的作用相同。
所有类型的所有对象默认都有 .toString() 方法,但你可以重定义之。

你不能把公有方法赋值给主对象,必须使用 prototype 属性,如同 beCool() shower() 方法[注意:原作者说是在他印象中]

就像我尝试使用 amputate() 函数,和显示 Person.prototype.legs 一样,prototype 属性是为所有“对象实例”所共享。访问 lk.legs 依旧是“2。无论如何, 尝试更改实例的 .legs 属性,可使用 gk.legs=1 或在 Person类中使用 this.lengs=1。这就是为什么,调用 gk.amputate() 方法只除去了 Gavin的一条腿,而不是 Lisa。要更改原型属性,你必须使用诸如 Person.prototype.legs=1 this.constructor.prototype.legs=1

可以这样定义匿名函数 foo = function(p1,p2){ some code }
使用 new Function() 如:foo = new Function('p1','p2','code');
在全局中定义可防止函数访问内部变量。

就象在前面代码中注释的那样,给 gk.race 设置某值并非重写私有变量 race的值。
你可以有公有和私有变量名为相同的变量,也就是两个名字相同但并非一样的变量, 如下面代码, foo this.foo 并非一样。

function StupidClass()
{
 
var foo = "internal";
 
this.foo = "external";
 
this.yell =
  
function()
  
{
   alert
("Internal foo is " + foo + "\nExternal foo is " + this.foo)
  
}
}


私有函数和特有方法跟私有变量和公共属性其实差不多,他们都是在 new object[创建新实例]时产生。所以,每当 new Person时,都有一份 makeOlder(), toString(), getName(), eat(), exercise(), weigh(), getRace(), getAge(), muchTimePasses() 的新拷贝。对比公有方法[beCool shower只有一份拷贝],公共方法可以节省 内存和提高效率。

注意,使用公有替代私有的代价,私有成员外部无法访问,因此代码结构比较强壮,但增加了内存和效率的负担;公有成员外部可访问和更改,因此代码不够强壮,但节省了内存的开销和提高了效率。

例如,在上面例子中的 age maxAge为私有变量; 在外部只能通过公有方法 getAge访问[只读] age,在外部无法访问 maxAge。当把它们更改为公有属性时,如 gk.maxAge=1; gk.age=200;那样的话在外部可以直接更改它们的值,但是 alive 变量将无法得到有效的控制。

二、      继承
在前面部分,我们看到怎样创建JS 类,包括 私有、特有、公有 的属性和方法。
本部分阐述 Javascript 中的继承。

摘要
你将看到如何继承, 如:
ChildClassName.prototype = new ParentClass();

你应当记住,要重置构造器,如:
ChildClassName.prototype.constructor=ChildClassName.

你只要在子类中使用 Function.call() 方法, 就可调用被继承类的方法,
Javascript
不支持受保护(protected)方法。

范例
下面的例子,演示两个类间的继承操作:

<script type="text/javascript">
/*<![CDATA[*/

function Mammal(name)
{
 
this.name = name;
 
this.offspring = [];
}

Mammal.
prototype.haveABaby =
 
function()
 
{
  
var newBaby = new Mammal("Baby " + this.name);
  
this.offspring.push(newBaby);
  
return newBaby;
 
}

Mammal.
prototype.toString =
 
function()
 
{
  
return '[Mammal "'+this.name+'"]';
 
}

function Cat(name)
{
 
this.name = name;
}

Cat.
prototype = new Mammal();
// 在这里发生继承 

Cat.
prototype.constructor = Cat;
// Cat 继承 Mammal 的构造器 

Cat.
prototype.toString =
 
function()
 
{
  
return '[Cat "'+this.name+'"]';
 
}

var someAnimal = new Mammal('Mr. Biggles');
alert
('someAnimal is ' + someAnimal);
// 结果为 someAnimal is [Mammal "Mr. Biggles"] someAnimal 隐含为 someAnimal.toString()

var myPet = new Cat('Felix');
alert
('myPet is ' + myPet);
// 结果为 myPet is [Cat "Felix"] myPet 隐含为 myPet.toString()

myPet.haveABaby
();
// 调用 继承自 Mammal 的方法 .haveABaby

alert
(myPet.offspring.length);
// shows that the cat has one baby now 

alert
(myPet.offspring[0]);
// 结果为 '[Mammal "Baby Felix"]',注意,这里 Mammal 如果正确的话,应当为 Cat

/*]]*/
</script>

 

使用 .constructor 属性
看看上面代码中的末行。Cat 的子类应当是 Cat[例子中为 Mammal]
当执行 haveABaby() 方法时,这个方法创建一个新的 Mammal
虽然我们可为 Cat 类创建一个新的 haveABaby() 方法,但是更好的方法是在被继承类中修正 haveABaby() 方法。

所有 JS 的对象实例都有一个 constructor 属性,他指向该对象的类。
如:someAnimal.<

@suches
http://btbtd.com/mods/blog/item.php?uid=1&item=3897

发表于 @ 2008-01-22 10:53:09

该文章暂时没有回复
评论内容:
昵称:
验证码: