JavaScript 权威指南(1):JavaScript面向对象简介
发布在蓝翔出品:JavaScript 权威指南2014年11月26日view:6157
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

这一系列有9篇文章,不是犀牛书的笔记,而是国外一些大牛的文章,我尽可能的翻译过来,当然有的可能已经有翻译好了的我就搬过来了,比如这篇就是在 火狐开发者社区上面有人翻译了(翻了一大半)。 JavaScript 的核心是支持面向对象的,同时它也提供了强大灵活的 OOP 语言能力。本文从对面向对象编程的介绍开始,带您探索 JavaScript 的对象模型,最后描述 JavaScript 当中面向对象编程的一些概念。

复习JavaScript

如果您对 JavaScript 的概念(如变量、类型、方法和作用域等)缺乏自信,您可以在重新介绍 JavaScript 这篇文章里学习这些概念。您也可以查阅这篇 JavaScript 1.5 核心指南

面向对象编程

面向对象编程是用抽象方式创建基于现实世界模型的一种编程模式。它使用先前建立的范例,包括模块化,多态和封装几种技术。今天,许多流行的编程语言(如Java,JavaScript,C#,C+ +,Python,PHP,Ruby和Objective-C)都支持面向对象编程(OOP)。

面向对象编程可以看作是使用一系列对象相互协作的软件设计,相对于传统观念,一个程序只是一些函数的集合,或简单的计算机指令列表。 在OOP中,每个对象能够接收邮件,处理数据和发送消息给其他对象。每个对象都可以被看作是一个独立的小机器有不同的作用和责任。

面向对象程序设计的目的是促进更好的编程灵活性和可维护性,并在大型软件工程中广为流行。凭借其十分注重的模块化,面向对象的代码开发更简单,往后维护更容易理解,使其自身能更直接的分析,编码,理解复杂的情况和过程,比非模块化编程方法省事。1

术语

Namespace 命名空间

允许开发人员在一个专用的名称下捆绑所有功能代码的容器。

Class 类

定义对象的特征。

Object 对象

类的一个实例。

Property 属性

对象的特征,比如颜色。

Method 方法

对象的能力,比如行走。

Constructor 构造函数

实例化时调用的函数。

Inheritance 继承

一个类可以继承另一个类的特征。

Encapsulation 封装

类定义了对象的特征,方法只定义了方法如何执行。

Abstraction 抽象

结合复杂的继承,方法,属性,一个对象能够模拟现实的模型。

Polymorphism 多态

多意为‘许多’,态意为‘形态’。不同类可以定义相同的方法或属性。 更多关于面向对象编程的描述,请参照维基百科的 面向对象编程 。

原型编程

基于原型的编程不是面向对象编程中体现的风格,且行为重用(在基于类的语言中也称为继承)是通过装饰它作为原型的现有对象的过程实现的。这种模式也被称为弱类化,原型化,或基于实例的编程。

原始的(也是最典型的)基于原型语言的例子是由大卫·安格尔和兰德尔·史密斯开发的。然而,弱类化的编程风格近来变得越来越流行,并已被诸如JavaScript,Cecil,NewtonScript,IO,MOO,REBOL,Kevo,Squeak(使用框架操纵Morphic组件),和其他几种编程语言采用。1

JavaScript面向对象编程

命名空间

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

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

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

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

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

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

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

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

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

// 给普通方法和属性创建一个叫做MYAPP.commonMethod的容器
MYAPP.commonMethod = {
  regExForName: "", // 定义名字的正则验证
  regExForPhone: "", // 定义电话的正则验证
  validateName: function(name){
    // 对名字name做些操作,你可以通过使用“this.regExForname”
    // 访问regExForName变量
  },

  validatePhoneNo: function(phoneNo){
    // 对电话号码做操作
  }
}

// 对象和方法一起申明
MYAPP.event = {
    addListener: function(el, type, fn) {
    //  代码
    },
   removeListener: function(el, type, fn) {
    // 代码
   },
   getEvent: function(e) {
   // 代码
   }

   // 还可以添加其他的属性和方法
}

//使用addListner方法的写法:
MYAPP.event.addListener("yourel", "type", callback);

标准内置对象

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

console.log(Math.random());

注意:T这里和接下来的例子都假设名为 console.log 的方法全局有定义。console.log 实际上不是 JavaScript 自带的。 查看 JavaScript 参考:全局对象 了解 JavaScript 内置对象的列表。

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

自定义对象

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

function Person() { } 
// 或
var Person = function(){ }

对象(类的实例)

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

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

function Person() { }
var person1 = new Person();
var person2 = new Person();

有两种为对象创建实例,请参考 Object.create 。

构造器

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

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

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

function Person() {
  alert('Person instantiated');
}

var person1 = new Person();
var person2 = new Person();

属性 (对象属性)

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

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

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

function Person(firstName) {
  this.firstName = firstName;
  alert('Person instantiated');
}

var person1 = new Person('Alice');
var person2 = new Person('Bob');

// Show the firstName properties of the objects
alert('person1 is ' + person1.firstName); // alerts "person1 is Alice"
alert('person2 is ' + person2.firstName); // alerts "person2 is Bob"

方法

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

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

function Person(firstName) {
  this.firstName = firstName;
}

Person.prototype.sayHello = function() {
  alert("Hello, I'm " + this.firstName);
};

var person1 = new Person("Alice");
var person2 = new Person("Bob");

// call the Person sayHello method.
person1.sayHello(); // alerts "Hello, I'm Alice"
person2.sayHello(); // alerts "Hello, I'm Bob"

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

function Person(firstName) {
  this.firstName = firstName;
}

Person.prototype.sayHello = function() {
  alert("Hello, I'm " + this.firstName);
};

var person1 = new Person("Alice");
var person2 = new Person("Bob");
var helloFunction = person1.sayHello;

person1.sayHello();                                 // alerts "Hello, I'm Alice"
person2.sayHello();                                 // alerts "Hello, I'm Bob"
helloFunction();                                    // alerts "Hello, I'm undefined" (or fails
                                                    // with a TypeError in strict mode)
alert(helloFunction === person1.sayHello);          // alerts true
alert(helloFunction === Person.prototype.sayHello); // alerts true
helloFunction.call(person1);                        // alerts "Hello, I'm Alice"

就像那个例子展示的, 我们对sayHello函数所引用的 — person1, Person.prototype, helloFunction 变量等.参考same function。这个函数调用时的值取决于我们怎么调用它。 当我们把它叫做一个表达式,我们从一个对象属性得到了这个函数 - person1.sayHello() - 这是我们从(PERSON1)获取的函数设置的对象,这也就是为什么person1.sayHello()使用的名称为“Alice”和person2.sayHello()使用的名称为“Bob”。但是,如果我们用其他方式调用,这就是不同的设置:从一个变量调用它 - helloFunction() - 此设置为全局对象(窗口,在浏览器)。由于该对象(可能)不具有FirstName属性,我们以Hello, I'm undefined结束。 (这是在松散模式的代码;它会在严格模式不同[错误],但为了避免混淆,我们不打算在这里详细)或者,我们可以把这个明确的使用Function#call(或Function#apply).

See more about this on Function#call and Function#apply

继承

继承是创建一个类为一个或多个类专用版本(JavaScript的只支持单继承)的一种方法。专门的类通常被称为子,另一类通常称为父。在JavaScript中你可以通过分配父类的一个实例的子类做到这一点,然后专攻它。在现代浏览器中,你还可以使用Object.create来实现继承。

JavaScript 没有检测到子类 prototype.constructor(参见Object.prototype的),所以我们必须手动指出。

在下面的例子中,我们定义 Studenrt 类作为一个Person 的子类。然后我们重新定义sayHello()方法,并添加sayGoodBye()方法。

// Define the Person constructor
function Person(firstName) {
  this.firstName = firstName;
}

// Add a couple of methods to Person.prototype
Person.prototype.walk = function(){
  alert("I am walking!");
};
Person.prototype.sayHello = function(){
  alert("Hello, I'm " + this.firstName);
};

// Define the Student constructor
function Student(firstName, subject) {
  // Call the parent constructor, making sure (using Function#call) that "this" is
  // set correctly during the call
  Person.call(this, firstName);

  // Initialize our Student-specific properties
  this.subject = subject;
};

// Create a Student.prototype object that inherits from Person.prototype.
// Note: A common error here is to use "new Person()" to create the Student.prototype.
// That's incorrect for several reasons, not least that we don't have anything to
// give Person for the "firstName" argument. The correct place to call Person is
// above, where we call it from Student.
Student.prototype = Object.create(Person.prototype); // See note below

// Set the "constructor" property to refer to Student
Student.prototype.constructor = Student;

// Replace the "sayHello" method
Student.prototype.sayHello = function(){
  alert("Hello, I'm " + this.firstName + ". I'm studying " + this.subject + ".");
};

// Add a "sayGoodBye" method
Student.prototype.sayGoodBye = function(){
  alert("Goodbye!");
};

// Example usage:
var student1 = new Student("Janet", "Applied Physics");
student1.sayHello();   // "Hello, I'm Janet. I'm studying Applied Physics."
student1.walk();       // "I am walking!"
student1.sayGoodBye(); // "Goodbye!"

// Check that instanceof works correctly
alert(student1 instanceof Person);  // true 
alert(student1 instanceof Student); // true

关于Student.prototype= Object.create(Person.prototype的); 注:对于没有Object.create旧的JavaScript引擎,可以使用一个 “polyfill”,或者可以使用函数,可实现相同的结果,如:

function createObject(proto) {
    function ctor() { }
    ctor.prototype = proto;
    return new ctor();
}

// Usage:
Student.prototype = createObject(Person.prototype);

See Object.create for more on what it does, and a shim for older engines.

封装

在上例中,Student 并不需要知道 Person 类的 walk()方法是如何实现的,但还是可以使用方法;Student类并不需要明确定义方法,除非我们想改变它。这就是所谓的封装,其中每一个类继承其父的方法,只有需要定义东西时希望它变化。

抽象

抽象是当前工作问题部分的模型工厂(不知咋翻)。这可以通过继承或组合来实现。JavaScript通过专业的继承或组合实现让类的实例等于其他对象的属性的值

JavaScript函数类从Object类继承(这说明模型的专业化),Function.prototype的属性是对象的一个实例。

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

多态

就像所有的方法和属性的都是在原型属性内部定义的,不同的类可以定义具有相同名称的方法;方法是作用于他们定义的类。这是唯一真正当两个类不持有父子关系(当一个类不从其他继承 继承链)。

笔记

The techniques presented in this article to implement object-oriented programming are not the only ones that can be used in JavaScript, which is very flexible in terms of how object-oriented programming can be performed.

Similarly, the techniques shown here do not use any language hacks, nor do they mimic other languages’ object theory implementations.

There are other techniques that make even more advanced object-oriented programming in JavaScript, but those are beyond the scope of this introductory article.

参考

Wikipedia. “Object-oriented programming”, http://en.wikipedia.org/wiki/Object-…ed_programming

翻译:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#The_methods

评论
发表评论
5年前
赞了此文章!
5年前
赞了此文章!
5年前
赞了此文章!
WRITTEN BY
PUBLISHED IN
蓝翔出品:JavaScript 权威指南

不是犀牛书的笔记,整理的国外几篇文章,找到有中文版的我就直接搬过来了,没有就靠谷歌翻译了。

麻麻问我怎么不开心

我说在我的想象中有个好网站

学习前端肯定棒

整个网络找遍所有的网站都没有

她说将来会找到的

时间时间会给我答案

蓝翔毕业后再次寻找依然没有发现

我想我必须要离开

当我正要走时我看到了一个前端乱炖

那就是我要的前端网站

我的前端网站牛逼牛逼最牛逼

打酱油的路上我情不自禁

学习 学习

. . .

我的收藏