javascript中的面向对象(object-oriented)编程-阿里云开发者社区

开发者社区> jiacai2050> 正文

javascript中的面向对象(object-oriented)编程

简介: <div class="markdown_views"> <p>本文原发于我的<a href="http://liujiacai.net/blog/2015/02/01/javascript-oop/">个人博客</a>,经多次修改放到csdn上,主要是做备份用,为了更好的阅读体验,请到我的个人博客上阅读。</p> <hr> <p>最近工作一直在用nodejs做开发,有了no
+关注继续查看

本文原发于我的个人博客,经多次修改放到csdn上,主要是做备份用,为了更好的阅读体验,请到我的个人博客上阅读。


最近工作一直在用nodejs做开发,有了nodejs,前端、后端、脚本全都可以用javascript搞定,很是方便。但是javascript的很多语法,比如对象,就和我们常用的面向对象的编程语言不同;看某个javascript开源项目,也经常会看到使用this关键字,而这个this关键字在javascript中因上下文不同而意义不同;还有让人奇怪的原型链。这些零零碎碎的东西加起来就很容易让人不知所措,所以,有必要对javascript这门语言进行一下深入了解。

我这篇文章主要想说说如何在javascript中进行面向对象的编程,同时会讲一些javascript这门语言在设计之初的理念。下面让我们开始吧。
首先强调一下,我们现在广泛使用的javascript都是遵循了ECMAScript 5.1标准的,正在制定中的版本为6.0,这个版本变化很大,增加了很多新的语法与函数,大家可以去Mozilla Developer Network上查看。

设计理念

javascript1.0 最初是由网景公司的Brendan Eich在1995年5月花了十天搞出来的,Eich的目标是设计出一种即轻量又强大的语言,所以Eich充分借鉴了其他编程语言的特性,比如Java的语法(syntax)、Scheme的函数(function)、Self的原型继承(prototypal inheritance)、Perl的正则表达式等。
其中值得一提的是,为什么继承借鉴了Self语言的原型机制而不是Java的类机制?首先我们要知道:
- Self的原型机制是靠运行时的语义
- Java的类机制是靠编译时的类语法

Javascript1.0的功能相对简单,为了在今后不断丰富javascript本身功能的同时保持旧代码的兼容性,javascript通过改变运行时的支持来增加新功能,而不是通过修改javascript的语法,这就保证了旧代码的兼容性。这也就是javascript选择基于运行时的原型机制的原因。
wikipedia这样描述到:JavaScript is classified as a prototype-based scripting language with dynamic typing and first-class functions。这些特性使得javascript是一种多范式解释性编程语言,支持面向对象,命令式(imperative), 函数式(functional)编程风格。

对象

在javascript中,除了数字、字符串、布尔值(true/false)、undefined这几个简单类型外,其他的都是对象。
数字、字符串、布尔值这些简单类型都是不可变量,对象是可变的键值对的集合(mutable keyed conllections),对象包括数组Array、正则表达式RegExp、函数Function,当然对象Object也是对象。
对象在javascript中说白了就是一系列的键值对。键可以是任何字符串,包括空串;值可以是除了undefined以外的任何值。在javascript中是没有类的概念(class-free)的,但是它有一个原型链(prototype linkage)。javascript对象通过这个链来实现继承关系。

javascript中有一些预定义对象,像是ObjectFunctionDateNumberStringArray等。

字面量(literal)

javascript中的每种类型的对象都可以采用字面量(literal)的方式创建。
对于Object对象,可以使用对象字面量(Object literal)来创建,例如:

var empty_object = {};//创建了一个空对象
//创建了一个有两个属性的对象
var stooge = {
    "first-name": "Jerome",
    "last-name": "Howard"
};

当然,也可以用new Object()Object.create()的方式来创建对象。
对于FunctionArray对象都有其相应的字面量形式,后面会讲到,这里不再赘述。

原型链(prototype linkage)

javascript中的每个对象都隐式含有一个[[prototype]]属性,这是ECMAScript中的记法,目前各大浏览器厂商在实现自己的javascript解释器时,采用的记法是__proto__,也就是说每个对象都隐式包含一个__proto__属性。举个例子:

var foo = {
    x: 10,
    y: 20
};

foo这个对象在内存中的存储结构大致是这样的:

basic-object

当有多个对象时,通过__proto__属性就能够形成一条原型链。看下面的例子:

var a = {
    x: 10,
    calculate: function (z) {
        return this.x + this.y + z;
    }
};
var b = {
    y: 20,
    __proto__: a
};
var c = {
    y: 30,
    __proto__: a
};
// call the inherited method
b.calculate(30); // 60
c.calculate(40); // 80

上面的代码在声明对象b、c时,指明了它们的原型为对象a(a的原型默认指向Object.prototye,Object.prototype这个对象的原型指向null),这几个对象在内存中的结构大致是这样的:


prototype-chain

这里需要说明一点,我们如果想在声明对象时指定它的原型,一般采用Object.create()方法,这样效率更高。
除了我们这里说的__proto__属性,相信大家平常更常见的是prototype属性。比如,Date对象中没有加几天的函数,那么我们可以这么做:

Date.prototype.addDays = function(n) {
    this.setDate(this.getDate() + n);
}

那么以后所有的Date对象都拥有addDays方法了(后面讲解继承是会解释为什么)。那么__proto__属性与prototype属性有什么区别呢?

javascript的每个对象都有__proto__属性,但是只有函数对象prototype属性。

那么在函数对象中, 这两个属性的有什么区别呢?
1. __proto__表示该函数对象的原型
2. prototype表示使用new来执行该函数时(这种函数一般成为构造函数,后面会讲解),新创建的对象的原型。例如:
var d = new Date();
d.proto === Date.prototype; //这里为true

看到这里,希望大家能够理解这两个属性的区别了。
在javascript,原型和函数是最重要的两个概念,上面说完了原型,下面说说函数对象。

函数对象Function

首先,函数在javascript中无非也是个对象,可以作为value赋值给某个变量,唯一不同的是函数能够被执行。
使用对象字面量方式创建的对象的__proto__属性指向Object.prototype(Object.prototype__proto__属性指向null);使用函数字面量创建的对象的__proto__属性指向Function.prototype(Function.prototype对象的__proto__属性指向Object.prototype)。
函数对象除了__proto__这个隐式属性外,还有两个隐式的属性:
1. 函数的上下文(function’s context)
2. 实现函数的代码(the code that implements the function’s behavior)

和对象字面量一样,我们可以使用函数字面量(function literal)来创建函数。类似于下面的方式:

//使用字面量方式创建一个函数,并赋值给add变量
var add = function (a, b) { 
    return a + b;
};

一个函数字面量有四个部分:
1. function关键字,必选项。
2. 函数名,可选项。上面的示例中就省略了函数名。
3. 由圆括号括起来的一系列参数,必选项。
4. 由花括号括起来的一系列语句,必选项。该函数执行时将会执行这些语句。

函数调用与this

一个函数在被调用时,除了声明的参数外,还会隐式传递两个额外的参数:thisarguments
this在OOP中很重要,this的值随着调用方式的不同而不同。javascript中共有四种调用方式:
1. method invocation pattern。当函数作为某对象一个属性调用时,this指向这个对象。this赋值过程发生在函数调用时(也就是运行时),这叫做late binding
2. function invocation pattern。当函数不作为属性调用时,this指向全局对象,这是个设计上的错误,正确的话,内部函数的this应该指向外部函数。可以通过在函数中定义一个变量来解决这个问题。

    var add = function(a, b) {return a+b;}
    var obj = {
        value: 3,
        double: function() {
            var self = this;//把this赋值给了self
            this.value = add(self.value, self.value);
        }
    }
    obj.double(); //obj.value现在为6
  1. construct invocation pattern。javascript是一门原型继承语言,这也就意味着对象可以直接从其他对象中继承属性,没有类的概念。这和java中的继承不一样。但是javascript提供了一种类似与java创建对象的语法。当一个函数用new来调用时,this指向新创建的对象。这时的函数通常称为构造函数。
  2. apply invocation pattern。使用函数对象的apply方法来执行时,this指向apply的第一个参数。

除了this外,函数在调用是额外传入的另一个参数是arguments。它是函数内部的一个变量,包含函数调用处的所有参数,甚至包含函数定义时没有的参数。

var sum = function () { 
    var i, sum = 0;
    for (i = 0; i < arguments.length; i += 1) {
        sum += arguments[i];
    }
    return sum;
};
sum(4, 8, 15, 16, 23, 42); // 108

需要注意的是,这里的arguments不是一个数组,它只是一个有length属性的类数组对象(Array-like),它并不拥有数组的其他方法。
关于对象,最后说一下数组,javascript中的数组和平常编程中的数组不大一样。

数组对象Array

数组是一种在内存中线性分配的数据结构,通过下标计算出元素偏移量,从而取出元素。数组应该是一个快速存取的数据结构,但是在javascript中,数组不具备这种特性。
数组在javascript中一个具有传统数组特性的对象,这种对象能够把数组下标转为字符串,然后把这个字符串作为对象的key,最后对取出对应该key的value(这又一次说明了对象在javascript中就是一系列键值对)。

虽然javascript中的数组没有传统语言中的数组那么快,但是由于javascript是弱类型的语言,所以javascript中的数组可以存放任何值。此外Array有很多实用的方法,大家可以去MDN Array查看。
javascript也为数组提供了很方便的字面量(Array Literal)定义方式:

var arr = [1,2,3]

通过数组字面量创建的数组对象的__proto__指向Array.prototype。

继承Inheritance

在Java中,对象是某个类的实例,一个类可以从另一个类中继承。但是在基于原型链的javascript中,对象可以直接从另一个对象创建。

在上面讲解对象时,我们知道了在创建一个对象时,该对象会自动赋予一个__proto__属性,使用各种类型的字面量(Literal)时,javascript解释器自动为__proto__进行了赋值。当我们在javascript执行使用new操作符创建对象时,javascript解释器在构造函数时,同时会执行类似于下面的语句

     this.__proto__ = {constructor: this};

新创建的对象都会有一个__proto__属性,这个属性有一个constructor属性,并且这个属性指向这个新对象。举个例子:

var d = new Date()
d.__proto__.constructor === Date //这里为true

如果new不是一个操作符,而是一个函数的话,它的实现类似于下面的代码:

Function.prototype.new =  function () {
    // Create a new object that inherits from the constructor's prototype.
    var that = Object.create(this.prototype);
    // Invoke the constructor, binding –this- to the new object.
    var other = this.apply(that, arguments);
    // If its return value isn't an object, substitute the new object.
    return (typeof other === 'object' && other) || that;
};

之前也说了,基于原型的继承机制是根据运行时的语义决定的,这就给我们提供了很大的便利。比如,我们想为所有的Array添加一个map函数,那么我们可以这么做:

Array.prototype.map = function(f) {
    var newArr = [];
    for(i=0; i<this.length; i++) {
        newArr.push(f(this[i]));
    }
    return newArr;
}

因为所有的数组对象的__proto__都指向Array.prototype对象,所以我们为这个对象增加方法,那么所有的数组对象就都拥有了这个方法。
javascript解释器会顺着原型链查看某个方法或属性。如果想查看某对象的是否有某个属性,可以使用Object.prototype.hasOwnProperty方法。

总结

通过上面多次讲解,希望大家对对象在javascript中就是一系列的键值对原型函数这三个概念有更加深刻的认识,使用javascript来写前端、后端与脚本。在React.js 2015大会上,Facebook公布了即将开源的React Native,这意味着今后我们可以用javascript来写IOS、Android的原生应用了,这可真是learn-once, write-anywhere。相信随着ECMAScript 6的发布,javascript这门语言还会有一系列翻天覆地的变化,Stay Tuned。:-)

参考

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《JavaScript面向对象编程指南(第2版)》——1.7 OOP小结
本节书摘来自异步社区《JavaScript面向对象编程指南(第2版)》一书中的第1章,第1.7节,作者:【加拿大】Stoyan Stefanov著,更多章节内容可以访问云栖社区“异步社区”公众号查看
1243 0
《JavaScript面向对象编程指南(第2版)》——导读
本书是《JavaScript面向对象编程指南》的第二版。前一版由Stoyan Stefanov著(Packet出版社发行),在业界广受好评。然而,自第一版发行至今已过了五个年头。期间,JavaScript由一项主要适用于浏览器客户端的计算机技术,逐渐发展成为一种多功能的程序设计语言,甚至连服务端也能由它来编写。
1171 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4478 0
JavaScript面向对象的编程指南学习笔记
JavaScript面向对象的编程指南学习笔记 第一章 1,用自己的语言描述出,什么是对象、类、封装、聚合、继承、多态? 对象,拥有属性和方法的任何抽象概念。 类,可以实例化,有共同属性或方法(行为)的相似对象,的模板 封装,提供接口,隐藏接口的实现 聚合,将大功能(对象)拆分成小功能(小对象)去实现的逆过程。
8816 0
《JavaScript面向对象编程指南(第2版)》——1.8 训练环境设置
通常情况下,控制台只提供单行输入,但我们可以用分号做分割符来执行多个JavaScript语句。而如果您需要更多行代码的话,也可以通过组合键shift+Enter来实现换行,在这种情况下代码不会被立即执行。
1579 0
JavaScript 面向对象编程之一
一:Class and private And public JS 中的类以 function 进行声明,同时 JS 也支持声明私有 private 和公有 public 成员,只不过跟 C# 不一样,它们不是使用这两个关键字实现的。
626 0
《JavaScript设计模式》——第2章 写的都是看到的——面向对象编程 2.1两种编程风格——面向过程与面向对象
面向对象编程就是将你的需求抽象成一个对象,然后针对这个对象分析其特征(属性)与动作(方法)。这个对象我们称之为类。面向对象编程思想其中有一个特点就是封装,就是说把你需要的功能放在一个对象里。比如你大学毕业你来公司携带的行李物品没有一件一件拿过来,而是要将他们放在一个旅行箱里,这样不论携带还是管理都会更方便一些。
1229 0
+关注
jiacai2050
http://liujiacai.net/
83
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载