谈论JavaScript对象——个人总结

前言 疑惑、怀疑与思考 JavaScript到底是面向对象还是基于对象

与其它的语言相比,JavaScript总是显得不那么合群。比如:

不同于其它的面向对象语言,JavaScript一直没有类的概念(ES6之前),ES6的到来也并没有改变它是基于原型的本质,这点是最让开发人员困惑的地方

_proto_ 和 prototype 傻傻分不清

对象可以是由 new 关键字实例化,也可以直接由花括号定义

JavaScript对象可以自由添加属性,而其他的语言不行

在被诟病和争论中,有人喊出JavaScript并非“非面向对象”的语言,而是“基于对象”的语言。但是,对于如何定义“面向对象”和“基于对象”,基本上很难有人能够回答。

JavaScript到底是否需要模拟类class?

这需要明白JavaScript语言的设计思想,才能更清楚到底是否需要模拟类,以及为什么需要模拟类。在早期人们习惯于其他语言的面向对象编程方式,而对JavaScript感到困惑,并尝试用贴近类的方式去编程。

溯源与再思考 什么是面向对象?

我们先看看JavaScript对对象的定义:“语言和宿主的基础设施由对象来提供,并且 JavaScript 程序即是一系列互相通讯的对象集合”。这里的意思根本不是表达弱化面向对象的意思,反而是表达对象对于语言的重要性。

到底什么是面向对象?Objcet在英文中,是一切事物的总称,这和面向对象编程的抽象思维有相通之处。中文翻译“对象”却没有这样的普适性。在不同的编程语言中,设计者也利用各种不同的语言特性来描述对象,最为成功的流派是使用“类”的方式来描述对象,这诞生了诸如C++、Java等流行的编程语言。

而JavaScript早年则选择了一个更为冷门的流派:原型。这是不合群的原因之一。

然而不幸的是,因为一些公司政治原因,JavaScript推出之时受管理层之命被要求模仿 Java,所以,JavaScript 创始人 Brendan Eich 在“原型运行时”的基础上引入了 new、this 等语言特性,使之“看起来更像 Java”。

因此,我们至少需要明白一件事,我们之前所熟知的“面向对象”的编程方式,其实是“基于类”的面向对象,它并不是面向对象的全部,确切地说,基于类只是面向对象编程的一个流派而已。而想要理解JavaScript对象,就必须清空我们认识“基于类的面向对象”相关的概念,回到人类对对象的朴素认识和无关语言的基础理论,我们就能够理解JavaScript面向对象设计的思路。

什么是原型?什么是类?

“基于类”的编程提倡使用一个关注分类和类之间关系开发模型。在这类语言中,总是先有类,再从类去实例化一个对象。类与类之间又可能会形成继承、组合等关系。类又往往与语言的类型系统整合,形成一定编译时的能力。

与此相对,“基于原型”的编程看起来更为提倡程序员去关注一系列对象实例的行为,而后才去关心如何将这些对象,划分到最近的使用方式相似的原型对象,而不是将它们分成类。

基于原型和基于类都能够满足基本的复用和抽象需求,但是适用的场景不太相同。这就像专业人士可能喜欢在看到老虎的时候,喜欢用猫科豹属豹亚种来描述它,但是对一些不那么正式的场合,“大猫”可能更为接近直观的感受一些。我们的 JavaScript 并非第一个使用原型的语言,在它之前,self、kevo 等语言已经开始使用原型来描述对象了。

JavaScript的原型与对象 JavaScript原型:

抛开模拟Java的复杂语法设施(new、Function Object、函数的prototype属性等),原型系统可以说相当简单,用两条可以概括:

如果所有对象都有私有字段 [[prototype]],就是对象的原型;

读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。

这个模型在以前的各个历史版本中并没有大的改变,在ES6提供了一些列内置函数,可以更直接地访问操作原型:

Object.create 根据指定的原型创建新对象,原型可以是 null;

Object.getPrototypeOf 获得一个对象的原型;

Object.setPrototypeOf 设置一个对象的原型

利用这三个方法,我们可以完全抛开类的思维,利用原型来实现抽象和复用。例如:

// 这段代码创建了一个“猫”对象,又根据猫做了一些修改创建了虎,之后我们完全可以用 Object.create 来创建另外的猫和虎对象,我们可以通过“原始猫对象”和“原始虎对象”来控制所有猫和虎的行为 var cat = { say(){ console.log("meow~"); }, jump(){ console.log("jump"); } } var tiger = Object.create(cat, { say:{ writable:true, configurable:true, enumerable:true, value:function(){ console.log("roar!"); } } }) var anotherCat = Object.create(cat); anotherCat.say(); var anotherTiger = Object.create(tiger); anotherTiger.say();

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywydj.html