面向对象的 JavaScript 编程:原理与实践

JavaScript Object-Oriented Programming: Principles and Practices

面向对象的JavaScript编程:原理与实践

1. 简介

初衷

笔者早年编写代码使用过 Motorola 68000 和 x86汇编、C/C++、Pascal、Fortran、PowerBuilder、Visual Basic,后来又短暂接触过Java、Python和Swift。最近这些年来使用最多的是JavaScript(正式名称是ECMAScript,本书经常会缩写为JS)。众所周知,JS问世之初是“难登大雅之堂”的小玩意儿,还要靠冠名Java以壮声势。但是随着浏览器作为软件发布、运行的平台成熟起来,再加上NodeJS项目的兴旺,JS本身的进步和普及程度已经使它成为每一个前台软件工程师和全栈工程师必不可少的工具。

虽然这种语言已经强大到适用于大多数类型的后台服务器程序(更不用说它是前台开发的唯一正式语言),但是还是有人诟病其不够严谨,争论它是不是完全符合面向对象(Object-Oriented)语言的特征。更多的初学者和程序员则是对JS的对象运作机制和使用多少有些含糊不清。这是因为JS的对象跟以前常见的C++或者Java的对象的确有所不同,甚至可以说JS的对象从最初就不像C++或者Java那样经过深思熟虑、严格定义的。但是幸运的是,发展到今天,JS对OO的支持足以实现绝大多数的编程需要。

本书并不想参与任何无谓的或者纯理论的讨论,只想理清JS的对象到底是怎么回事,以及怎么使用JS最有效地面向对象编程,和它的局限性在哪儿。

感谢

非常感谢 Jiang Hao 提供的宝贵意见。

适合的读者

本书不是写给初学者看的。读者需要有一定的JS基础以及“知其然且知其所以然”的态度。如果你学习或者使用了一段时间JavaScript,但是对有些概念还是理解得似是而非,这本书就是试图讲解那些概念背后的来龙去脉(当然这些信息在 ES6 Specification 里都有,但是我相信大多数读者不会喜欢去读那些枯燥的文档)。如果你以前用 C++ 和 Java,对JavaScript的对象、函数、继承、类等等总感觉有些别扭,那这本书可以帮你澄清这些容易混淆的概念。如果你没有 C++ 或者 Java 语言的基础,可以说是喜忧参半:喜的是你不会把 JavaScript 和 C++/Java 里同名不同义的概念混淆,没有先入为主的思维定势;忧的是有些面向对象的概念你可能需要补一补课。

我没有凑字数给出版社的压力,所以本书的内容基本全是干货——我故意避免有任何废话、显而易见的的描述。但是有的概念我会重复好几次,这或者是因为此概念非常重要,或者是因为它不那么容易理解。我相信大多数人和我一样,一个复杂的概念并不是看一遍就过目不忘、运用自如。

所有代码都已使用 Node 6.x LTS 环境或者在最新版本的Chrome浏览器里验证过。这些代码绝大多数都是可以从头跑到尾、并且可在 console 里看到输出结果的。我非常鼓励你把这些代码运行通过后,再按自己的想法改一改,看看结果是不是你期望的。

另外需要抱歉的是有些英文专用单词也许翻译得不是最常见的用法。任何技术上或者翻译上的错误,或者讲述不够清楚、不够细致的地方,还望读者不吝赐教。

引用资料

本书的动力之一就是我读了 Nicolas Zakas 的 《The Principles of Object-Oriented JavaScript》 所受到的启发。有些内容的组织结构和代码举例可能会引用其书。本书 永久免费 ,但是我也鼓励有英文阅读能力和财力的读者购买 Zakas 的书学习。

其它好的参考书籍和网站我会逐步列出。

2. 基础数据类型(Primitive)和引用数据类型(Reference)

和任何其它现代的编程语言一样,JS对普通的算数运算和循环都有很容易理解的使用方法。JS语言本身的与众不同之处很大程度上在于它对数据的表达和处理。根据其在内存里的存储和管理方式,JS支持的数据类型可以分为两大类:基础数据类型(Primitive)和引用数据类型(Reference)。二者的区别对于我们理解对象有很关键的作用。

基础数据类型

基础数据类型用于存储比较简单这几类数据:

boolean:布尔值,取值范围只有 和

number:任何整数和小数(浮点数)。

:warning:

JS里另有 这种对象,与此处number数据类型的关系后面会提及。

string:字符串,包括一个字符(JS没有'char'类型),支持Unicode。

:warning:

跟 类似,JavaScript 里也有一种标准对象 对应

null:“空”。此类型只有一个值就是

undefined:“未定义”。此类型只有一个值就是 ,最常见的场景是你的代码定义了一个变量但是还没有赋值时,其值为

symbol:在ES6里引入的一种新数据类型,用于定义对象成员的键值,跟面向对象编程关系也不大,在本书里就不讲了。希望有机会再写一本ES6的书吧。

所有的基础数据类型都是一个具体的值存储于内存某处,而你定义的变量就直接被赋予这个值(而不是指向具体值的地址;这是和引用类型最大的区别)。而每一个变量所存贮的值并不跟其它变量共享,即便其二者的内容是一样的。比如,

// stringsvarname ="Jack";varanswer ="a";// numbersvarcount =25;varresult =-0;

vartotalCost =34.56;// booleanletdefault=true;// nullvarobj =null;// undefinedletk =undefined;

letq;// 自动被赋值为 undefined// 每个变量都有自己的存贮地址letval1 =50;

letval2 = val1;

// val2 现在也将‘50’这个值存于自己的存储空间内val1 =25;console.log(val2);

// 50;val2的值并不随着val1而改变;注意跟后边引用数据类型的例子对比识别基础数据类型

由于JS的语法规定,在定义一个变量的时候,并不需要声明变量类型,其后赋值时也不进行类型检查,所以有时候程序本身需要动态地检查其所持有的值的类型。为此JS提供了 操作符:

// 继续上面的代码console.log(typeofname);// 'string'console.log(typeofcount);// 'number'console.log(typeoftotalCost);// 'number'console.log(typeofdefault);// 'boolean'console.log(typeofk);// 'undefined'console.log(typeofq);// 'undefined'console.log(typeofobj);// 'object' 这个结果有问题

以上这段代码通俗易懂,但是有两点需要说明:

严格来说, 是作用于其后变量当时所被赋予的值,而不是变量本身。因为JS对变量本身并没有定义数据类型。

最后一句里,变量 的值是 ,但是 返回的值却是 。这已经被确认为一个JS语言里的bug。但是因为这个bug已经存在很久了,如果改正反倒可能让一些以前运行正常的程序出错。所以这个bug就被一直保留了下来。

在我们的代码里正确判断 也很简单:

//如何判断一个值为nullconsole.log(obj ===null);

//trueconsole.log(k ===null);

//false; kisundefined带强制类型转换的比较

当你用双等号 比较两个不同基础数据类型的值时,JavaScript引擎会试图进行强制数据转换之后再进行比较,而三等号 不进行强制类型转换,JavaScript引擎会既比较数据类型也比较数据值。比如

console.log("5"==5);// trueconsole.log("5"===5);// falseconsole.log(undefined==null);// trueconsole.log(undefined===null);// false

如果双等号的效果恰好是你需要的,当然这样的代码会比你自己转换数据类型后再比较更简洁。反之这会是不那么容易找到的bug。最好的办法是养成习惯:每次写比较语句都先问自己一下:该使用双等号还是三等号?除非你确定双等号真的是你需要的,都应该使用三等号。

。。。。。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180125A041ZV00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券