JavaScript面向对象编程

命名空间

命名空间是一个容器,它允许开发人员在一个独特的,特定于应用程序的名称下捆绑所有的功能。 在JavaScript中,命名空间只是另一个包含方法,属性,对象的对象。

需要认识到非常重要的一点,与其他面向对象编程语言中的普通对象和命名空间相比,它们在语言层面上没有区别。

创造的JavaScript命名空间背后的想法很简单:一个全局对象被创建,所有的变量,方法和功能成为该对象的属性。使用命名空间也最大程度地减少应用程序的名称冲突的可能性。

我们来创建一个全局变量叫做 MYAPP

  1. // 全局命名空间
  2. var MYAPP = MYAPP || {};
 

在上面的代码示例中,我们首先检查MYAPP是否已经被定义(是否在同一文件中或在另一文件)。如果是的话,那么使用现有的MYAPP全局对象,否则,创建一个名为MYAPP的空对象用来封装方法,函数,变量和对象。

我们也可以创建子命名空间:

  1. // 子命名空间
  2. MYAPP.event = {};
 

下面是用于创建命名空间和添加变量,函数和方法的代码写法:

  1. // 给普通方法和属性创建一个叫做MYAPP.commonMethod的容器
  2. MYAPP.commonMethod = {
  3. regExForName: "", // 定义名字的正则验证
  4. regExForPhone: "", // 定义电话的正则验证
  5. validateName: function(name){
  6. // 对名字name做些操作,你可以通过使用“this.regExForname”
  7. // 访问regExForName变量
  8. },
  9. validatePhoneNo: function(phoneNo){
  10. // 对电话号码做操作
  11. }
  12. }
  13. // 对象和方法一起申明
  14. MYAPP.event = {
  15. addListener: function(el, type, fn) {
  16. // 代码
  17. },
  18. removeListener: function(el, type, fn) {
  19. // 代码
  20. },
  21. getEvent: function(e) {
  22. // 代码
  23. }
  24. // 还可以添加其他的属性和方法
  25. }
  26. //使用addListner方法的写法:
  27. MYAPP.event.addListener("yourel", "type", callback);
 

标准内置对象

JavaScript有包括在其核心的几个对象,例如,Math,Object,Array和String对象。下面的例子演示了如何使用Math对象使用其随机()方法来获得一个随机数。

  1. console.log(Math.random());
 
注意:T这里和接下来的例子都假设名为 console.log 的方法全局有定义。console.log 实际上不是 JavaScript 自带的。

查看 JavaScript 参考:全局对象 了解 JavaScript 内置对象的列表。

JavaScript 中的每个对象都是 Object 对象的实例且继承它所有的属性和方法。

自定义对象

JavaScript是一种基于原型的语言,它没类的声明语句,比如C+ +或Java中用的。这有时会对习惯使用有类申明语句语言的程序员产生困扰。相反,JavaScript可用方法作类。定义一个类跟定义一个函数一样简单。在下面的例子中,我们定义了一个新类Person。

  1. function Person() { }
  2. // 或
  3. var Person = function(){ }
 

对象(类的实例)

我们使用 new obj 创建对象 obj 的新实例, 将结果(obj 类型)赋值给一个变量方便稍后调用。

在下面的示例中,我们定义了一个名为Person的类,然后我们创建了两个Person的实例(person1 and person2).

  1. function Person() { }
  2. var person1 = new Person();
  3. var person2 = new Person();
 
有两种为对象创建实例,请参考 Object.create 。

构造器

在实例化时构造器被调用 (也就是对象实例被创建时)。构造器是对象中的一个方法。 在JavaScript,中函数就可以作为构造器使用,因此不需要特别地定义一个构造器方法. 每个声明的函数都可以在实例化后被调用执行

构造器常用于给对象的属性赋值或者为调用函数做准备。 在本文的后面描述了类中方法既可以在定义时添加,也可以在使用前添加。

在下面的示例中, Person类实例化时构造器调用一个 alert函数。

  1. function Person() {
  2. alert('Person instantiated');
  3. }
  4. var person1 = new Person();
  5. var person2 = new Person();
 

属性 (对象属性)

属性就是 类中包含的变量;每一个对象实例有若干个属性. 为了正确的继承,属性应该被定义在类的原型属性 (函数)中。

可以使用 关键字 this调用类中的属性, this是对当前对象的引用。 从外部存取(读/写)其属性的语法是: InstanceName.Property; 这与C++,Java或者许多其他语言中的语法是一样的 (在类中语法 this.Property 常用于set和get属性值)

在下面的示例中,我们为定义Person类定义了一个属性 firstName 并在实例化时赋初值。

  1. function Person(firstName) {
  2. this.firstName = firstName;
  3. alert('Person instantiated');
  4. }
  5. var person1 = new Person('Alice');
  6. var person2 = new Person('Bob');
  7. // Show the firstName properties of the objects
  8. alert('person1 is ' + person1.firstName); // alerts "person1 is Alice"
  9. alert('person2 is ' + person2.firstName); // alerts "person2 is Bob"
 

方法

方法与属性很相似, 不同的是:一个是函数,另一个可以被定义为函数。 调用方法很像存取一个属性,  不同的是add () 在方法名后面很可能带着参数. 为定义一个方法, 需要将一个函数赋值给类的 prototype 属性; 这个赋值给函数的名称就是用来给对象在外部调用它使用的。

在下面的示例中,我们给Person类定义了方法 sayHello(),并调用了它.

  1. function Person(firstName) {
  2. this.firstName = firstName;
  3. }
  4. Person.prototype.sayHello = function() {
  5. alert("Hello, I'm " + this.firstName);
  6. };
  7. var person1 = new Person("Alice");
  8. var person2 = new Person("Bob");
  9. // call the Person sayHello method.
  10. person1.sayHello(); // alerts "Hello, I'm Alice"
  11. person2.sayHello(); // alerts "Hello, I'm Bob"
 

在JavaScript中方法通常是一个绑定到对象中的普通函数, 这意味着方法可以在其所在context之外被调用。 思考下面示例中的代码:

  1. function Person(firstName) {
  2. this.firstName = firstName;
  3. }
  4. Person.prototype.sayHello = function() {
  5. alert("Hello, I'm " + this.firstName);
  6. };
  7. var person1 = new Person("Alice");
  8. var person2 = new Person("Bob");
  9. var helloFunction = person1.sayHello;
  10. person1.sayHello(); // alerts "Hello, I'm Alice"
  11. person2.sayHello(); // alerts "Hello, I'm Bob"
  12. helloFunction(); // alerts "Hello, I'm undefined" (or fails
  13. // with a TypeError in strict mode)
  14. alert(helloFunction === person1.sayHello); // alerts true
  15. alert(helloFunction === Person.prototype.sayHello); // alerts true
  16. helloFunction.call(person1); // alerts "Hello, I'm Alice"
 

如上例所示, 所有指向sayHello函数的引用 ,包括 person1Person.prototype, 和 helloFunction 等, 均引用了相同的函数.

在调用函数的过程中,this的值取决于我们怎么样调用函数.  在通常情况下,我们通过一个表达式person1.sayHello()来调用函数:即从一个对象的属性中得到所调用的函数。此时this被设置为我们取得函数的对象(即person1)。这就是为什么person1.sayHello() 使用了姓名“Alice”而person2.sayHello()使用了姓名“bob”的原因。

然而我们使用不同的调用方法时, this的值也就不同了。当从变量 helloFunction()中调用的时候, this就被设置成了全局对象 (在浏览器中即window)。由于该对象 (非常可能地) 没有firstName 属性, 我们得到的结果便是"Hello, I'm undefined". (这是松散模式下的结果, 在 严格模式中,结果将不同(此时会产生一个error)。 但是为了避免混淆,我们在这里不涉及细节) 。另外,我们可以像上例末尾那样,使用Function#call (或者Function#apply)显式的设置this的值。

更多有关信息请参考 Function#call and Function#apply

继承

创建一个或多个类的专门版本类方式称为继承(Javascript只支持单继承)。 创建的专门版本的类通常叫做子类,另外的类通常叫做父类。 在Javascript中,继承通过赋予子类一个父类的实例并专门化子类来实现。在现代浏览器中你可以使用 Object.create 实现继承.

JavaScript 并不检测子类的 prototype.constructor (见 Object.prototype), 所以我们必须手动申明它.

在下面的例子中, 我们定义了 Student类作为 Person类的子类. 之后我们重定义了sayHello() 方法并添加了 sayGoodBye() 方法.

  1. // 定义Person构造器
  2. function Person(firstName) {
  3. this.firstName = firstName;
  4. }
  5. // 在Person.prototype中加入方法
  6. Person.prototype.walk = function(){
  7. alert("I am walking!");
  8. };
  9. Person.prototype.sayHello = function(){
  10. alert("Hello, I'm " + this.firstName);
  11. };
  12. // 定义Student构造器
  13. function Student(firstName, subject) {
  14. // 调用父类构造器, 确保(使用Function#call)"this" 在调用过程中设置正确
  15. Person.call(this, firstName);
  16. // 初始化Student类特有属性
  17. this.subject = subject;
  18. };
  19. // 建立一个由Person.prototype继承而来的Student.prototype对象.
  20. // 注意: 常见的错误是使用 "new Person()"来建立Student.prototype.
  21. // 这样做的错误之处有很多, 最重要的一点是我们在实例化时
  22. // 不能赋予Person类任何的FirstName参数
  23. // 调用Person的正确位置如下,我们从Student中来调用它
  24. Student.prototype = Object.create(Person.prototype); // See note below
  25. // 设置"constructor" 属性指向Student
  26. Student.prototype.constructor = Student;
  27. // 更换"sayHello" 方法
  28. Student.prototype.sayHello = function(){
  29. alert("Hello, I'm " + this.firstName + ". I'm studying " + this.subject + ".");
  30. };
  31. // 加入"sayGoodBye" 方法
  32. Student.prototype.sayGoodBye = function(){
  33. alert("Goodbye!");
  34. };
  35. // 测试实例:
  36. var student1 = new Student("Janet", "Applied Physics");
  37. student1.sayHello(); // "Hello, I'm Janet. I'm studying Applied Physics."
  38. student1.walk(); // "I am walking!"
  39. student1.sayGoodBye(); // "Goodbye!"
  40. // Check that instanceof works correctly
  41. alert(student1 instanceof Person); // true
  42. alert(student1 instanceof Student); // true
 

对于“Student.prototype = Object.create(Person.prototype);”这一行,在不支持 Object.create方法的老JavaScript引擎中,可以使用一个"polyfill"(又名"shim",查看文章链接),或者使用一个function来获得相同的返回值,就像下面:

  1. function createObject(proto) {
  2. function ctor() { }
  3. ctor.prototype = proto;
  4. return new ctor();
  5. }
  6. // Usage:
  7. Student.prototype = createObject(Person.prototype);
 
更多相关信息请参考 Object.create,连接中还有一个老JavaScript引擎的兼容方案(shim)。

封装

在上一个例子中,Student类虽然不需要知道Person类的walk()方法是如何实现的,但是仍然可以使用这个方法;Student类不需要明确地定义这个方法,除非我们想改变它。 这就叫做封装,对于所有继承自父类的方法,只需要在子类中定义那些你想改变的即可。

抽象

抽象是允许模拟工作问题中通用部分的一种机制。这可以通过继承(具体化)或组合来实现。
JavaScript通过继承实现具体化,通过让类的实例是其他对象的属性值来实现组合。

JavaScript Function 类继承自Object类(这是典型的具体化) 。Function.prototype的属性是一个Object实例(这是典型的组合)。

  1. var foo = function(){};
  2. alert( 'foo is a Function: ' + (foo instanceof Function) ); // alerts "foo is a Function: true"
  3. alert( 'foo.prototype is an Object: ' + (foo.prototype instanceof Object) ); // alerts "foo.prototype is an Object: true"
 

多态

就像所有定义在原型属性内部的方法和属性一样,不同的类可以定义具有相同名称的方法;方法是作用于所在的类中。并且这仅在两个类不是父子关系时成立(继承链中,一个类不是继承自其他类)。

注意

本文中所展示的面向对象编程技术不是唯一的实现方式,在JavaScript中面向对象的实现是非常灵活的。

同样的,文中展示的技术没有使用任何语言hacks,它们也没有模仿其他语言的对象理论实现。

JavaScript中还有其他一些更加先进的面向对象技术,但这些都超出了本文的介绍范围。

文章出处:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

JavaScript面向对象编程[转]的更多相关文章

  1. JavaScript面向对象编程学习笔记

    1  Javascript 面向对象编程 所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量.对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例 ...

  2. 快速学习JavaScript面向对象编程

    到处都是属性.方法,代码极其难懂,天哪,我的程序员,你究竟在做什么?仔细看看这篇指南,让我们一起写出优雅的面向对象的JavaScript代码吧! 作为一个开发者,能否写出优雅的代码对于你的职业生涯至关 ...

  3. 深入理解Javascript面向对象编程

    深入理解Javascript面向对象编程 阅读目录 一:理解构造函数原型(prototype)机制 二:理解原型域链的概念 三:理解原型继承机制 四:理解使用类继承(继承的更好的方案) 五:建议使用封 ...

  4. 【转】Javascript 面向对象编程(一):封装

    原文链接:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html Javascript ...

  5. Javascript 面向对象编程(一):封装 by 阮一峰

    <Javascript高级程序设计(第二版)>(Professional JavaScript for Web Developers, 2nd Edition) 它们都是非常优秀的Java ...

  6. 转:javascript面向对象编程

    作者: 阮一峰 日期: 2010年5月17日 学习Javascript,最难的地方是什么? 我觉得,Object(对象)最难.因为Javascript的Object模型很独特,和其他语言都不一样,初学 ...

  7. 探讨javascript面向对象编程

    (个人blog迁移文章.) 前言: 下面将探讨javascript面向对象编程的知识. 请不要刻意把javascript想成面向对象编程是理所当然的. javascript里面,对象思想不可少,但是不 ...

  8. JavaScript面向对象编程(一)原型与继承

    原型(prototype) JavaScript是通过原型(prototype)进行对象之间的继承.当一个对象A继承自另外一个对象B后,A就拥有了B中定义的属性,而B就成为了A的原型.JavaScri ...

  9. JavaScript面向对象编程(二)构造函数和类

    new关键字和构造函数 在文章JavaScript面向对象编程(一)原型与继承中讨论啦JavaScript中原型的概念,并且提到了new关键字和构造函数.利用new关键字构造对象的实例代码如下: // ...

  10. JavaScript 面向对象编程(三)如何写类和子类

    在JavaScript面向对象编程(一)原型与继承和JavaScript面向对象编程(二)构造函数和类中,我们分别讨论了JavaScript中面向对象的原型和类的概念.基于这两点理论,本篇文章用一个简 ...

随机推荐

  1. python 文件移动

    python实现文件移动: import shutil shutil.move("original_path", "new_folder") # move fi ...

  2. js正则表达使用实例

    (1)替换掉htmlStr中所有的<font..>和</font> var htmlstr='<font color="#fff">ABC< ...

  3. java script 模拟鼠标事件

    try { var selector1 = "._3-8y:first-child"; var evt = document.createEvent("MouseEven ...

  4. 在winsshd 中添加id_rsa.pub 实现Windows 服务器主机自动信任Linux 客户端

    文章一. 生成密钥: 在Linux主机(ssh客户端),通过ssh-keygen在建立SSH keys# ssh-keygen -t rsa (连续三次回车,即在本地生成了公钥和私钥,不设置密码)将在 ...

  5. golang 打印变量类型

    fmt.Println("type:", reflect.TypeOf(err.Error()))

  6. Pocket Cube

    Pocket Cube http://acm.hdu.edu.cn/showproblem.php?pid=5983 Time Limit: 2000/1000 MS (Java/Others)    ...

  7. 二级联动的作业&左右移动作业

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 基于快速排序的数组划分:2组 3组 K组(sort color)大小写排序 · Partition Array

    2组: [抄题]: 给出一个整数数组 nums 和一个整数 k.划分数组(即移动数组 nums 中的元素),使得: 所有小于k的元素移到左边 所有大于等于k的元素移到右边 返回数组划分的位置,即数组中 ...

  9. 有关于mfc webbrowser插件的使用

    最近写的东西中常常需要嵌入一些浏览器,微软提供了一个比较好的接口,可以在MFC写的程序中嵌入一个简易的浏览器,是以ActiveX插件的形式提供的接口,使用起来也比较的方便,这里我就简单记录下这个插件的 ...

  10. spring框架之AspectJ的XML方式完成AOP的开发

    1. 步骤一:创建JavaWEB项目,引入具体的开发的jar包 * 先引入Spring框架开发的基本开发包 * 再引入Spring框架的AOP的开发包 * spring的传统AOP的开发的包 * sp ...