`
sslaowan
  • 浏览: 374106 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论
阅读更多

从网上找到的一些关于javascript的讲解,重要是因为看好多人的代码看不懂,终于在csdn和别的网站上找到一些,但是因为不会使用trackback,所以全文粘过来了,请作者不要介意.

/************************************************************************************************

JavaScript 面向对象编程, 第一部分: 继承

作者:lzlhero <lzlhero_at_eyou.com>
发布:heiyeluren <heiyeluren_at_gmail.com>
时间:2005-08-25

我们将向你展示 JavaScript 如何实现面向对象的语言中的: 继承. 同时, 这些例子将向你展示如何实现类的封装. 在此, 我们不会讨论多态实现.

虽然 JavaScript 是脚本语言, 但它所支持的面向对象编程也是非常强大的. 虽然它没有类和实例, 但它有对象, 原型和隐式的继承. 我们将会解释如何模拟继承及其超类与子类之间关系的形式. 原型是理解继承概念的关键, 我们将会教你如何建立原型, 如何检测一个对象是否是另外一个对象的原型, 及其 JavaScript 的模型与 Java 面向对象编程之间的区别. 我们同样会向你展示如何检测对象所包含的各种属性的方法. 在另外一篇文章里, 我还会详细地讲解有关 "原型链 (prototype chain)" 的知识.

本文大量地参考了 Webreference.com 中 "Object-Oriented Programming with JavaScript, Part I: Inheritance" 的内容, 许多内容我进行了详细的测试和再探讨, 以保证内容不会有太大的失误.
原文地址: http://www.webreference.com/js/column79/

面向对象语言的特点

面向对象设计是基于以下 3 个主要原理的: 封装, 继承和多态. 说某种程序语言是支持 OO (面向对象) 设计的, 只有在它的语法中支持以上 3 个概念才可以这么说. 这种语言应该为你提供某些方法, 以使你能很轻松地定义和使用这些范例. 封装涉及到了将某个对象变成一个 "黑盒子"的概念. 当你使用某个对象时, 你不用知道它内部是如何工作的, 你也不必理解对象是如何工作的. 这个对象只需将它绝对有用的信息以接口方式提供出来. 此对象应该给你提供友好的接口, 来让你可以使用其有限的属性集和方法集. 封装还有一层意思, 那就是说某个对象包含了它需要的每一样东西, 这包括数据和对于它的操作. 封装的概念非常的强大, 因为它允许将一个大的软件项目有效地分配给每个开发人员, 对于团队中的每个人, 他们只需要关注自己所实现的对象, 而不需要太多地关注于别人的实现. 开发项目中的开销使得开发团队中成员与接口的数量按指数级别增长. 封装是自 "软件危机" 以来最受欢迎的 OO 设计理念.

软件的复用是 OO 设计思想中另外一个重要的特点. 在软件体系中实现此思想的主要方法就是继承. 类就是定义对象的功能. 超类是某个新类, 或者说是子类被建立的来源类. 一个子类从它的超类中继承了所的方法和属性. 实际上, 所有的子类都是被自动地生成的, 因此节省了大量的工作. 你不需要一个一个地定义这些子类. 当然, 你可以重载那些继承下来的方法和属性. 事实上, 谁也没有指出哪个子类要建立得和其超类一模一样, 除非你没有重载任何的属性和方法.

多态可能是这个 3 个概念中最复杂的一个了. 其本质上是说, 每个对象都可以处理各种不同的数据类型. 你不必为处理不同的数据类型而建立不同的类. 其典型的例子就是画图的类, 你不必为实现画圆, 画矩形, 画椭圆而编写不同的类. 你可以建立一个足够聪明的类来调用特定的方法来操作特定的形状.

通过函数实现继承

虽然 JavaScript 不支持显示继承操作符, 但你可以通过其实方式实现隐式继承. 对于实现类的继承, 有 2 种比较常用的方式. 第一种将某个类定义成子类的方法是, 通过在负责定义子类函数的内部调用超类的构造函数. 看下面的示例:

// 超类构造函数
function superClass() {
  this.bye = superBye;
  this.hello = superHello;
}

// 子类构造函数
function subClass() {
  this.inheritFrom = superClass;
  this.inheritFrom();
  this.bye = subBye;
}

function superHello() {
  return "Hello from superClass";
}
 
function superBye() {
  return "Bye from superClass";
}

function subBye() {
  return "Bye from subClass";
}

// 测试构造特性的函数
function printSub() {
  var newClass = new subClass();
  alert(newClass.bye());
  alert(newClass.hello());
}

当你运行上面的 printSub 函数时, 它会依次执行 subBuy 和 superHello 函数. 我们可以看到, bye 和 hello 方法最先在 superClass 中被定义了. 然而, 在 subClass 中, bye 方法又被重载了, subClass 构造函数头两行的功能只是做了一个简单的原始的继承操作, 但它是通过显示执行 inheritFrom 方法来完成的继承操作. 继承的过程先是将 superClass 的对象原型赋给 subClass 下的 inheritFrom 方法, 然后在执行完 superClass 的构造函数后, superClass 的属性就被自动地加到了 subClass 的属性列表中.这主要是由于在 subClass 中通过 this 来调用的 inheritFrom (也就是 superClass) 构造函数造成的, 通过此种方式调用 superClass 构造函数时, JavaScript 解释器会把 superClass 中的 this 与 subClass 中的 this 理解成位于同一个作用域下的 this 关键字, 所以就产生了继承的效果.

另外, 需要说明的是, 对于任何一个实例化的对象, 你任意地为它添加属性或方法, 如下所示:

  var newClass = new subClass();
  newClass.addprop = "added property to instance object";

很明显, 通过此种方式添加的属性和方法只对当前实例化对象有效, 不会影响所有的同类型对象实例. 无疑, 它是你创造的一个独一无二的对象实例.

通过原型实现继承

第二种, 也是更强大的方法是通过建立一个超类对象, 然后将其赋值给子类对象的 prototype 属性, 以此方式来建立子类的继承. 假设我们的超类是 superClass, 子类是 subClass. 其 prototype 的赋值格式如下:

subClass.prototype = new superClass;

对于原型继承的实现方式, 让我们刚前面的代码改写一下, 示例如下:

// 超类构造函数
function superClass() {
  this.bye = superBye;
  this.hello = superHello;
}

// 子类构造函数
function subClass() {
  this.bye = subBye;
}
subClass.prototype = new superClass;

function superHello() {
  return "Hello from superClass";
}
 
function superBye() {
  return "Bye from superClass";
}

function subBye() {
  return "Bye from subClass";
}

// 测试构造特性的函数
function printSub() {
  var newClass = new subClass();
  alert(newClass.bye());
  alert(newClass.hello());
}

我们可以看到, 除了将前面第一种继承方式中 subClass 中的前 2 行内容, 换成函数外的 prototype 赋值语句之外, 没有其它任何的变化, 但代码的执行效果和前面是一样的.

为已经建立的对象添加属性

通过原型实现的继承比通过函数实现的继承更好, 因为它支持动态继承. 你可以在构造函数已经完成之后, 再通过 prototype 属性定义超类的其它方法和属性, 并且其下的子类对象会自动地获得新的方法和属性. 下面是示例, 你可以看到它的效果.

function superClass() {
  this.bye = superBye;
  this.hello = superHello;
}

function subClass() {
  this.bye = subBye;
}
subClass.prototype = new superClass;

function superHello() {
  return "Hello from superClass";
}
 
function superBye() {
  return "Bye from superClass";
}

function subBye() {
  return "Bye from subClass";
}

var newClass = new subClass();

/*****************************/
// 动态添加的 blessyou 属性
superClass.prototype.blessyou = superBlessyou;

function superBlessyou() {
  return "Bless You from SuperClass";
}
/*****************************/

function printSub() {
  alert(newClass.bye());
  alert(newClass.hello());
  alert(newClass.blessyou());
}

这就是我们经常看到的为内部对象, 如 String, Math 等再添加其它属性和方法的技巧. 对于任何的内部对象和自定义对象, 你都也可以通过 prototype 来重载其下的属性和方法. 那么在调用执行时, 它将调用你所定义的方法和属性. 下面是示例:

// 为内部 String 对象添加方法
String.prototype.myMethod = function(){
  return "my define method";
}

// 为内部 String 对象重载方法
String.prototype.toString = function(){
  return "my define toString method";
}

var myObj = new String("foo");

alert(myObj.myMethod());
alert(myObj);
alert("foo".toString());

另外需要注意的是, 所有 JavaScript 内部对的 prototype 属性都是只读的. 你可以像上面那样为内部对象的原型添加或重载属性和方法,但不能更改该内部对象的 prototype 原型. 然而, 自定义对象可以被赋给新的原型. 也就是说, 像下面这样做是没有意思的.

function Employee() {
  this.dept = "HR";
  this.manager = "John Johnson";
}

String.prototype = new Employee;

var myString = new String("foo");

上面的程序在运行之后不会报错, 但显然, 如果你调用 myString.dept 将会得到一个非定义的值.

另外, 一个经常使用的是 prototype 下的 isPrototypeOf() 方法, 它主要用来判断指定对象是否存在于另一个对象的原型链中. 语法如下:

object1.prototype.isPrototypeOf(0bject2);

上面的格式是用来判断 Object2 是否出现 Object1 的原型链中. 示例如下:

function Person() {
  this.name = "Rob Roberson";
  this.age = 31;
}

function Employee() {
  this.dept = "HR";
  this.manager = "John Johnson";
}

Employee.prototype = new Person();

var Ken = new Employee();

当执行 Employee.prototype.isPrototypeOf(Ken), Person.prototype.isPrototypeOf(Ken) 和 Object.prototype.isPrototypeOf(Ken) 时, 结果都会返回 true.

用于 Netscape 下的特定继承检测

在 Netscape 浏览器 4.x 到 6, 及其 Mozilla 系列浏览中, JavaScript 将对象间的原型关系存储在一个特殊的内部属性对象中, __proto__ (前后是 2 个下划线). 下面是一个示例:

function Shape() {
  this.borderWidth = 5;
}

function Square() {
  this.edge = 12;
}

Square.prototype = new Shape;

myPicture = new Square;

alert(myPicture.__proto__);
alert(myPicture.borderWidth);

由于脚本执行过 Square.prototype = new Shape 语句, 所以 myPicture 具有了一个指向 Shape 对象的内部属性 __proto__. 在脚本的执行过程中, 当要获取对象的某个属性值, 并且此对象是通过原型赋值而建立的某个对象, 在自身并没有对某个属性进行定义时, JavaScript 解析器会查看它的 __proto__ 属性对象, 也就它的原型对象, 然后枚举其原型中的所有属性, 而得出的结果要么是有这个属性, 要么是没有这个属性. 如果没有此属性, 再枚举原型对象下面的原型对象, 直到此过程真正的结束. 而所有的这些 JavaScript 引擎内部的操作, 我们是不会知道的, 下面的内容就是对这个问题的解释.

其实, 对于所有的自定义对象, 无论它有没有使用过 prototype 赋值操作, 它都具有一个 __proto__ 内部对象. 而如果某个对象是通过多层 prototype "继承" 来的, 所有的 "继承" 而来的属性却可以通过简单的一层循环遍历出来, 而不需要使用什么递归算法, 因为 JavaScript 引擎自动给我们做了. 示例如下:

function Shape() {
  this.borderWidth = 5;
}

function Square() {
  this.edge = 12;
}

function RoundSquare()
{
  this.radio = 0.5;
}

Square.prototype = new Shape;
RoundSquare.prototype = new Square;

var myPicture = new RoundSquare;

for (property in myPicture.__proto__) {
alert(property);
}

我们或者还可以通过更改后面的循环, 来遍历某个子类对象继承来的所有属性, 如下:

for (property in RoundSquare.prototype) {
alert(property);
}

如果你不怕麻烦, 我们甚至还可以通过级连的方式, 取出其构造函数中定义的原始属性值.

alert(myPicture.__proto__.__proto__.borderWidth);

无论你是否修改过此属性值, 通过上面语句所取出的属性值都是原始定义值. 让我们沿着这个思路再往下看, 下面的代码涉及到另外一个问题, 这个问题和原型链 (prototype chain) 有关. 代码如下:

function State() {
}

function City() {
}
City.prototype = new State;

function Street() {
}

Street.prototype = new City;

var UniversityAvenue = new Street();

function tryIt() {
  alert(UniversityAvenue.__proto__== Street.prototype);
  alert(UniversityAvenue.__proto__.__proto__==
   City.prototype);
  alert(UniversityAvenue.__proto__.__proto__.__proto__
   == State.prototype);
  alert(UniversityAvenue.__proto__.__proto__.__proto__.
   __proto__== Object.prototype);
  alert(UniversityAvenue.__proto__.__proto__.__proto__.
   __proto__.__proto__== null);
}

当执行 tryIt 函数时, 所有的显示均为 true. 也就是说, 子类对象的 prototype.__proto__ 总是等于超类对象的 prototype 属性; 超类对象的 prototype.__proto__ 总是等于 Object.prototype; Object.prototype.__proto__ 总是为 null; 而实例对象的 __proto__ 总是等于其类对象的 prototype, 这就是为什么任何自定义对象都具有 __proto__ 属性的原因. 对于上面的叙述, 其对应的代码如下:

Street.prototype.__proto__ == City.prototype     // true
State.prototype.__proto__ == Object.prototype    // true
Object.prototype.__proto__ == null               // true
UniversityAvenue.__proto__ == Street.prototype    // true

模拟实现 instanceOf 函数

根据上一节的内容, 我们了解了有关 Netscape 所支持的 __proto__ 特性的内容. 这一节, 我们将利用此特性来创建自己的实例对象检测函数.

许多时候, 我们都需要判断某个对象是否是由某个类来定义的, 在其它的语言里, 你可以通过 instanceOf 函数来实现此判断. 在 JavaScript 中同样提供了一个 instanceof 运行符, 而在 __proto__ 的基础上, 我们完全可以自己定义一个同样的函数, 虽然这看上去是在重复劳动, 但有助于我们更深刻地了解有关 __proto__ 的知识. 下面的代码只是用来说明功能, 在实际的应用中, 你不需要重复定义 instanceOf 函数, 使用 instanceof 运算符即可.

function instanceOf(object, constructorFunction) {
  while (object != null) {
    if (object == constructorFunction.prototype)
     {return true}
    object = object.__proto__;
  }
  return false;
}
function State() {
}

function City() {
}
City.prototype = new State;

function Street() {
}

Street.prototype = new City;

var UniversityAvenue = new Street();
function demo() {
  alert("instanceOf(UniversityAvenue, Street) is " +
   instanceOf(UniversityAvenue, Street));
  alert("instanceOf(UniversityAvenue, City) is " +
   instanceOf(UniversityAvenue, City));
  alert("instanceOf(UniversityAvenue, State) is " +
   instanceOf(UniversityAvenue, State));
}

你会看到所有的运行结果全部为 true, 其原理和上一节的级连判断相等如出一辙. 实际证明, 它的运行结果和 instanceof 运行符的运行结果是一致的.

你可以通过 constructor 属性来检测任意对象的超类, 此属性返回通过 new 运算符创建新对象时所调用的构造函数, 返回值是 Function 对象类型. 因为 Object 内部对象是支持 constructor 属性的, 并且有的对象 (包括内部对象和自定义对象) 都是由 Object 继承而来的, 所以所有的对象都支持此属性. 让我们再看一下下面的例子:

function Employee() {
  this.dept = "HR";
  this.manager = "John Johnson";
}

function printProp() {
  var Ken = new Employee();
  alert(Ken.constructor);
}

调用完 printProp 函数之后, 你会看到弹出框中显示的是 Employee 函数的定义文本, 其实 Ken.constructor 的返回值本身是 Function 对象类型, 而在 alert 时被隐含地调用了 toString 方法. 对于类对象本身, 你同样可以调用 prototype.constructor 来取出其构造函数.

对象的分类和打印

JavaScript 支持 3 种主要类型的对象: 内部对象, 宿主对象, 自定义对象, 可能还有特殊的外部对象, 如: ActiveX 对象或 XPCOM 对象. 内部对象被 JavaScript 语言本身所支持, 如: Object, Math, Number 对象等. 所有的内部对象的共同特点是以大写字母开头, 并且它们是大小写敏感的. 如果你想使用数学常量 PI, 必须写成 Math.PI, 你如果写成 math.PI, JavaScript 会显示错误. 宿主对象是被浏览器支持的, 目的是为了能和被浏览的文档可以交互, 如: document, window 和 frames. 宿主对象的特点是所有对象全部以小写字母开头. 因为 JavaScript 本身就是大小写敏感的, 所以你同样不能将大小写搞混. 剩下要说的就只是自定义对象了, 你可以随便将你的对象定义成小写或大小写, 但是一定要符合基本的命名规范. 如下所示, 这就是一个自定义对象:

function employee() {
  this.dept = "HR";
  this.manager = "John Johnson";
}

function printProp() {
  var ken = new Employee();
  for (property in ken) {
    alert(property);
  }
}

前面我们已经提到过, 所有的内部对象和自定义对象都是从 Object 对象继承而来的, 它是所有对象的超类对象. 你可建立一个 Object 对象的实例. 如下:

var myObject = new Object();

Object 类型的对象有许多的属性和方法, 你可以查看相关的手册. 上面只是定义了一个最简单的空对象, 你还可以为 Object 构造函数传入参数, 它会返回相应类型值的实例化对象. 记住, 返回值的类型是某种对象类型的 (如: String, Number 或 Object). 这种方式和直接通过赋值字符串或数值常量不同, 主要表示在类型方面. 如下所示:

var myObject = new Object("foo"); // 返回值类型为 object
var myObject = new String("foo"); // 返回值类型为 object, 效果同上

var myObject = "foo";             // 返回值类型为 string

你可以从调试器的 type 列中看出这个细微的差别, 它是简单类型与对象类型之间的区别. 但是, 你通过 alert 调用是看出不这些内部差别的, 因为在调用 alert 的过程中, 所有的对象类型值都会被自动调用 toString 方法进行字符串类型转换, 转换规则在 JavaScript 手册中有说明. 如果你 alert 的是某个自定义对象, 并且它没有定义 toString 方法, 那么它的返回值将为 "[object Object]". 对于 Math 对象, 当你查看其 Math.constructor 属性时, 你会得到一个不同于其它内部对象的内容为 "function Object()..." 的对象构造函数, 这与其它对象返回 "function Function()..." 的构造函数很不相同. 原因很简单, 因为 Math 对象是不能通过 new 运算符进行创建的.

另外, 如果传入 Object 构造函数中的值是一个对象, 它将原封不动地将该对象返回. 记住, 此操作只是一个引用, 而不是复制.

请求对象的属性

在前面的示例代码中, 已经出现过以循环方式枚举对象属性的示例. 其实, 通过 for...in 语句, 无论是任何对象和数组, 其下的元素, 属性和方法都可以遍历出来. 示例如下:

function employee() {
  this.dept = "HR";
  this.manager = "John Johnson";
}

function printProp() {
  var ken = new employee();
  for (property in ken) {
    alert(property + " : " + ken[property]);
  }
}

在遍历测试过程中, 你会发现, 对于自定义对象和宿主对象一般都可以枚举出其下的属性, 而对于内部对象, 几乎没有什么属性可以遍历出来, 为什么要说几乎呢? 因为对于 Mozilla 内核的浏览和 IE 内核的浏览器, 其 JavaScript 引擎有不同, Mozilla 下可以枚举出部分内容, 而枚举的原则不得而知.

对于每一个对象, 你还可以使用 hasOwnProperty 方法来检测其是否具有某个属性或方法. 由于 hasOwnProperty 是 Object 对象下的方法, 因此所有的对象都具有此方法. 但是, 需要注意的是, 此方法只能检测通过 this 关键字定义的成员, 如果某个成员是通过原型链定义的, 那么此方法将返回 false. 也就是说, 通过 prototype 继承来的属性和方法, 及其通过 prototype 定义的属性和方法, 都是不能通过 hasOwnProperty 来进行检测的. 由此, 我们可以看出, 通过 this关键字定义的属性和方法是同对象本身处于同一个地址空间内的; 而通过 prototype 定义的属性和方法, 是通过所谓的 "原型链" 进行管理的, 其下的的属性和方法不位于同一个地址空间之间, 当其调用这种属性或方法时, 必须通过 "链表" 才能索引到其下的某个属性或方法. 也就说, 调用以原型方式定义的属性和方法会有一个类似于链表的 "回溯" 操作.

和 hasOwnProperty 差不多, 对于对象中的每个属性, 我们还可以通过 propertyIsEnumerable 来测试它是否可以被枚举出来. 如下所示:

function Employee1() {
  this.dept = "HR";
  this.manager = "John Johnson";
  this.month = new Array("jan", "feb", "mar");
}

var Ken = new Employee1();

Ken.month.propertyIsEnumerable(0);

我们可以看到, 其语法是 propertyIsEnumerable 后跟数组的元素索引或对象中的属性名称. 同样, 对于原型链中的属性或方法它是不予考虑的, 结果当然是返回 false.

对于 JavaScript 和 Java 的比较

与 Java 这种基于类的语言不同, JavaScript 是一种基于原型的语言. 这种特点影响着每一个方面. 如术语 instance 在基于类的语言中有着特殊的意义, 它表示某个实例是隶属于某个特殊类的独立个体, 是对类定义的真实实现; 而在 JavaScript 中, 术语 instance 没有这个意思, 因为在它的语法里面, 类和实例是没有区别的. 虽然, 实例可以用来说明某个对象是使用某个特殊的构造函数生成的. 如下所示:

function superClass() {
  this.bye = superBye;
  this.hello = superHello;
}

function subClass() {
  this.bye = subBye;
}
subClass.prototype = new superClass;

function superHello() {
  return "Hello from superClass";
}
 
function superBye() {
  return "Bye from superClass";
}

function subBye() {
  return "Bye from subClass";
}

var newClass = new subClass();

newClass 是 subClass 的实例, 它是通过 subClass 的构造函数生成的. 而如果使用基于类的语言呢, 如下所示是 Java 的等价实现.

public class superClass {
  public superClass () {
    this.bye = superBye;
    this.hello = superHello;
  }
}
public class subClass extends superClass {
  public subClass () {
    this.bye = subBye;
  }
}

结束语

我们在上面的几节中, 详细地说明了有关 JavaScipt 中的面向对象实现, 或者只能说是模拟实现. 在此期间向你展示了实现方法. 并且阐述了有关如何检测对象之间关系的方法, 如何打印属性和测试某个特定的属性, 还做了一个 JavaScript 和 Java 的简单比较. 但这显然是不够的, 因而, JavaScript 的面向对象编程是非常多样化的, 格式也非常繁杂. 我打算在后面的内容里, 再总结一下有问封装的格式问题, 着重说明与对象方法有关的内容和实现, 同时还有上面提到的原型链 (prototype chain) 问题.

/**************************************************************************************************

javascript的面向对象编程

前言: 这篇教程是由Tim Scarfe 著,由javascriptKit.com 编辑目录结构.

在写面向对象的WEB应用程序方面JavaSciprt是一种很好的选择.它能支持OOP.因为它通过原型支持继承的方式和通过属性和方法的方式一样好.很多开发者试图抛弃JS,试着用C#或JAVA仅是因为JS不是他认为合适的面向对象的语言.许多人还没有认识到javascript支持继承.当你写面向对象的代码时.它能给你很强大的能量.你也可以使用它写出可复用,可封装的代码.


对象为何如此伟大?
面向对象思想的成功是由于它仿照了现实中的事物和事物的联系.事物有属性和方法.如果我们描述一个台灯.我们会说它的高度和宽度,比如12CM.开灯这个动作是它的方法.当它是处于开着的状态时.它可以被调亮一点或暗一点(也就是亮度这个属性值变大或变小).


javascript 给予了为WEB应用程序创建对象的能力.对象在需要的时候相应的事件会被触发,代码封装后,它能被实例化很多次.


用 new Object() 来创建对象


javascript里有几种创建对象的方法,在不同的场合可用不同的方法.最简单的就是用 new 操作符,例如:
<script language=javascript type=text/javascript>
<!--


person = new Object()
person.name = Tim Scarfe
person.height = 6Ft


person.run = function() {
this.state = running
this.speed = 4ms^-1
}


//-->
</script>


我们在这个例子里定义了person这个对象,然后加入了它的属性和方法.在这个例子里,自定义的方法里有两个属性.


用文字记号Literal Notation创建对象


用文字记号也可以创建对象,但要javascript 1.2以上版本.它的创建形式是多样的.


<script language=javascript type=text/javascript>
<!--


// Object Literals


timObject = {
property1 : Hello,
property2 : MmmMMm,
property3 : [mmm, 2, 3, 6, kkk],
method1 : function(){alert(Method had been called + this.property1)}
};


timObject.method1();
alert(timObject.property3[2]) // will yield 3


var circle = { x : 0, y : 0, radius: 2 } // another example


// nesting is no problem.
var rectangle = {
upperLeft : { x : 2, y : 2 },
lowerRight : { x : 4, y : 4}
}


alert(rectangle.upperLeft.x) // will yield 2


//-->
</script>


文字记号可是是数组,也可以是任意的javascript表达式或值.


用 new 操作符或文字记号创建一个自定义对象都是简单的,也是符合逻辑的.但它最大的缺点就是结果不可复用.也不能很容易的用不同的版本初始化创建对象.例如上面的第一个例子,如果 person 的 name 不是 Tim Scarfe,那样我们不得不重新定义整个对象,仅仅为了适应它的一点点改变.


对象的构造和原型


在OOP的世界里,用先前的方法定义对象在许多场合都有限制.我们需要一种创建对象的方法,类型可以被多次使用而不用重新定义.对象在实例化时每次都可以按需分配不同的值.实现这个目标的标准方法是用对象构造器函数.


一个对象构造器只不过是个有规则的javascript函数,它就象一个容器(定义参数,调用其他函数等等).它们两者所不同的是构造器函数是由 new 操作符调用的.(你将在下面的例子中看到).基于函数语法形式的对象定义,我们可以使它工作得最好.



让我们用现实世界中的猫来举个例子.猫的 name 和 color 是猫的属性.meeyow(猫叫)是它的一个方法.重要的是任何不同的猫都可能有不同的 name 和 meeyow 的叫声.为了建立适应这些特征的对象类,我们将使用对象构造器.
<script language=javascript type=text/javascript>
<!--


function cat(name) {
this.name = name;
this.talk = function() {
alert( this.name + say meeow! )
}
}


cat1 = new cat(felix)
cat1.talk() //alerts felix says meeow!


cat2 = new cat(ginger)
cat2.talk() //alerts ginger says meeow!


//-->
</script>


在这里,函数 cat() 是一个对象构造器,它的属性和方法在函数体里用this来定义,用对象构造器定义的对象用 new 来实例化.主意我们如何非常容易的定义多个cat 的实例.每一个都有自己的名字,这就是对象构造器带给我们的灵活性.
构造器建立了对象的蓝图.并不是对象本身.



在原型里增加方法.
在上面我们看到的例子里,对象的方法是在构造器里定义好的了.另外一种实现的途径是通过原型(prototype).xxx
原型是javascript继承的一种形式.我们可以为对象定义好后,再创造一个方法.原来所有对象的实例都将共享.


让我们来扩展最初的 cat 对象.增加一个改名的方法.用 prototype 的方式.
<script language=javascript type=text/javascript>
<!--


cat.prototype.changeName = function(name) {
this.name = name;
}


firstCat = new cat(pursur)
firstCat.changeName(Bill)
firstCat.talk() //alerts Bill says meeow!


//-->
</script>


就象你所看到的.我们仅只用了 关键字 prototype 实现了在对象定义后马上增加了changeName方法.这个方法被所有的实例共享.


用原型(prototype) 重载 javascript 对象


原型 在自定义对象和有选择性的重载对象上都可以工作.比如 Date() 或 String


 


这可能是无止境的.


子类和超类
在JAVA 和C++里,有关于类层次的外在概念.每一个类能有一个角色.
In Java and C++, there is an explicit concept of the class hierarchy. i.e. Every class can have a super class from which it inherits properties and methods. Any class can be extended, or sub-classed so the resulting subclass can inherit its parent's behavior. As we have seen, javascript supports prototype inheritance instead of class based. It's possible for inheritance to happen other ways, however.


The following is an example of inheritance through functions.


下面一个例子演示了如何继承通过function .
<script language=javascript type=text/javascript>
<!--


// thanks to webreference


function superClass() {
this.supertest = superTest; //attach method superTest
}


function subClass() {
this.inheritFrom = superClass;
this.inheritFrom();
this.subtest = subTest; //attach method subTest
}


function superTest() {
return superTest;
}

function subTest() {
return subTest;
}



var newClass = new subClass();


alert(newClass.subtest()); // yields subTest
alert(newClass.supertest()); // yields superTest

//-->
</script>


基于继承的原型是遥远的.为 javascript 应用程序在许多场合.
(基于原型的继承在许多javascript的应用场合是非常有用的.)


对象作为联合数组
正如你所知, (.)操作符能够用来存储.[] 操作符用来操作数组.


<script language=javascript type=text/javascript>
<!--


// These are the same
object.property
object[property]


//-->
</script>


<SCRIPT LANGUAGE=javascript>
<!--
function Circle (xPoint, yPoint, radius) {
this.x = xPoint;
this.y = yPoint;
this.r = radius;
}


var aCircle = new Circle(5, 11, 99);
alert(aCircle.x);
alert(aCircle[x]);
//-->
</SCRIPT>


 


 



How do I loop through properties in an object?
You need to use a for/in loop.
我们可以通过for in循环来遍历对象的属性。


<script language=javascript type=text/javascript>
<!--


var testObj = {


prop1 : hello,
prop2 : hello2,
prop3 : new Array(hello,1,2)
}


for(x in testObj) alert( x + - + testObj[ x ] )


//-->
</script>


<SCRIPT LANGUAGE=javascript>
<!--
var Circle = { x : 0, y : 1, radius: 2 } // another example


for(p in Circle)
alert( p + - + Circle[ p ] )


//-->
</SCRIPT>



The important thing to notice is that in the object syntax the property is an identifier, whereas in the array syntax, it's a string. The obvious benefits of using an array syntax to access an object is because of the literal data type, you can easily concat strings and play around with them to access an object. For this to work with the standard syntax, an eval() would need to be used.
应该值得注意的是对象的属性只是一个标识字符,尽管在一个数组里是一个字符串,因为是一个literal的数据类型,所以有利于使用数组的方式的操作一个对象。你也可以很容易的存取一个对象在标准的语句中。这个时候eval()函数可能用得到。


 


<script language=javascript type=text/javascript>
<!--


testObj = {


prop1 : hello,
prop2 : hello2,
prop3 : new Array(helloa,1,2)
}


for(x in testObj) alert( x + - + testObj[ x ] )
var prop3 = testObj[prop3];
alert(prop3);
//alert(prop[1]);
alert(typeof(prop3));
alert(eval(prop3)[1]);
alert(typeof(eval(prop3)[1]));


//-->
</script>

 



分享到:
评论

相关推荐

    深入理解JavaScript系列

    本书是一本全面、深入介绍JavaScript语言的学习指南。本书共分四个部分,第1部分帮助读者快速入手,掌握基本的JavaScript编程要点;第2部分介绍JavaScript的发展和技术背景;第3部分深入探索JavaScript,介绍了语法...

    《JavaScript程序设计案例教程》课件(共13章)第1章 JavaScript程序设计案例教程.pdf

    《JavaScript程序设计案例教程》课件(共13章)第1章 JavaScript程序设计案例教程.pdf《JavaScript程序设计案例教程》课件(共13章)第1章 JavaScript程序设计案例教程.pdf《JavaScript程序设计案例教程》课件(共13章)第...

    JavaScript宝典(第7版)

    经典畅销书 JavaScript宝典(第7版) 将帮您实现这些夙愿,它在大量示例代码和可运行脚本的引导下,指导您快速掌握JavaScript基础知识,并制定出最符合预定网站目标的策略。通过认真研读本书,您将可以编写脚本来实现...

    [JavaScript权威指南(第6版)]

    《JavaScript权威指南(第6版)》主要讲述的内容涵盖JavaScript语言本身,以及Web浏览器所实现的JavaScript API。本书第6版涵盖了HTML5和ECMAScript 5,很多章节完全重写,增加了当今Web开发的最佳实践的内容,新增...

    JavaScript完全学习手册

    资源名称:Javascript完全学习手册内容简介:本书分4篇14章,介绍Javascript的知识,全书内容包括:Javascript语法基础、流程控制、函数、内置对象编程、文档对象模型DOM与事件驱动、处理XML、...

    JavaScript之Math对象(源代码)

    JavaScript之Math对象(源代码)JavaScript之Math对象(源代码)JavaScript之Math对象(源代码)JavaScript之Math对象(源代码)JavaScript之Math对象(源代码)JavaScript之Math对象(源代码)JavaScript之Math对象...

    JavaScript基础语法(ppt)

    JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)JavaScript基础语法(ppt)...

    JavaScript从入门到精通(第2版)( (美)Steve Suehring)中文pdf

    《JavaScript从入门到精通》分为三篇:第一篇为JavaScript语法基础,主要包括JavaScript简介、如何实现JavaScript、数据类型、变量和常量、运算符、语句和函数等内容;第二篇为JavaScript对象,主要包括对象、核心...

    JavaScript宝典(第7版)( (美)Danny Goodman) 高清中文扫描版.pdf

    《javascript宝典(第7版)》内容简介:您想创建更具魅力的交互网页吗?您想更好地控制浏览器呈现内容的方式吗?经典畅销书们avascript宝典(第7版)》将帮您实现这些夙愿,它在大量示例代码和可运行脚本的引导下,指导您...

    JavaScript函数(源代码)

    JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)JavaScript函数(源代码)...

    JavaScript基于微信小程序的音乐播放器源码.zip

    JavaScript基于微信小程序的音乐播放器源码JavaScript基于微信小程序的音乐播放器源码JavaScript基于微信小程序的音乐播放器源码JavaScript基于微信小程序的音乐播放器源码JavaScript基于微信小程序的音乐播放器源码...

    JavaScript教程--从入门到精通

    JavaScript的出现,它可以使得信息和用户之间不仅只是一种显示和浏览的关系,而是实现了一种实时的、动态的、可交式的表达能力 . JavaScript脚本正是满足这种需求而产生的语言。它深受广泛用户的喜爱的欢迎。它是...

    Javascript 面向对象的JavaScript进阶

    Javascript 面向对象的JavaScript进阶 Javascript技术

    基于JavaScript实现的程序员表白爱心代码(2套)

    基于JavaScript实现的程序员表白爱心代码(2套) 基于JavaScript实现的程序员表白爱心代码(2套) 基于JavaScript实现的程序员表白爱心代码(2套) 基于JavaScript实现的程序员表白爱心代码(2套) 基于JavaScript实现的...

    javascript特效javascript特效javascript特效

    javascript特效javascript特效javascript特效javascript特效javascript特效javascript特效javascript特效javascript特效

    JavaScript语言参考手册

    本书是 JavaScript 语言的参考手册,包括核心语言中的对象和客户端、服务器端的扩展。JavaScript 是 Netscape 跨平台的基于对象的适合于客户和服务器的脚本语言。 你应该已经知道的东西 在哪里能找到 JavaScript ...

    JavaScript开发积分兑换小程序项目源代码.zip

    JavaScript开发积分兑换小程序项目源代码JavaScript开发积分兑换小程序项目源代码JavaScript开发积分兑换小程序项目源代码JavaScript开发积分兑换小程序项目源代码JavaScript开发积分兑换小程序项目源代码。...

    JavaScript权威指南(第6版).JavaScript:The.Definitive.Guide

    中文名: JavaScript权威指南 (第6版) 原名: JavaScript: The Definitive Guide: Activate Your Web Pages, 6th edition 作者: David Flanagan 版本: 英文文字版-pdf/EPUB + 完整书中源代码 出版社: O'Reilly 书号: ...

    JavaScript—The Definitive Guide, 6th Edition[JavaScript权威指南](英文版)

    经典的JavaScript教程 《O'Reilly精品图书系列:JavaScript权威指南(第6版)》讲述的内容涵盖JavaScript语言本身,以及Web浏览器所实现的JavaScriptAPI。《O'Reilly精品图书系列:JavaScript权威指南(第6版)》涵盖了...

Global site tag (gtag.js) - Google Analytics