当前位置: 首页 > Web前端 > HTML5

JavaScript是面向对象的还是基于对象的?

时间:2023-04-05 12:51:16 HTML5

相对于其他语言,JavaScript中的“对象”一向显得不那么合群。一些新手在学习JavaScript面向对象时,往往会有疑惑:为什么JavaScript(直到ES6)有对象的概念,而没有像其他语言那样有类的概念?为什么可以在JavaScript对象中自由添加属性,而在其他语言中却不行?甚至在一些争论中,有人强调JavaScript不是“面向对象的语言”,而是“基于对象的语言”,他们都无法回答“如何定义面向对象和基于对象”的问题。如果你在自学中遇到困难,想找一个前端学习环境,可以加入我们的前端学习圈,点我加入,会节省很多时间,减少很多学习中遇到的困难。事实上,基于对象和面向对象这两个形容词都出现在各种版本的JavaScript标准中。我们可以先看看JavaScript标准中基于对象的定义。这个定义的具体内容是:“语言和宿主基础设施是由对象提供的,ECMAScript程序是一系列相互通信的对象集合。”这里的意思根本不是表达弱化的面向对象的意思,而是表达对象对于语言的重要性。因此,在本文中,我将尝试让您了解JavaScript中的面向对象和面向对象的真正含义。JavaScript是面向对象的还是基于对象的?什么是对象?我们先来说说什么是对象。因为翻译,我们很难理解中文语境下“物”的真正含义。其实,英文中的Object(对象)是万物的总称,这与面向对象程序设计的抽象思维有共同之处。中国的“物”不具备这样的普遍性。在学习编程的过程中,我们更多的是把它理解为一个专业术语。但无论如何,我们应该认识到,对象并不是计算机领域凭空产生的概念,它是沿着人类思维方式产生的抽象(所以面向对象编程也被认为是:一种更接近到人类思维模式编程范式)。那么,我们先来看看,在人的思维模式中,什么是对象。JavaScript是面向对象的还是基于对象的?对象的概念形成于人类的童年早期,远早于我们编程逻辑中常用的值、过程等概念。小时候,我们总是先意识到一个苹果可以吃(这里的某个苹果是一个对象),然后意识到所有的苹果都可以吃(这里所有的苹果都是一个类),然后我们才能实现连接在三个苹果和三个梨之间,出现了数字“3”(价值)的概念。在《面向对象分析与设计》一书中,GradyBooch为我们做了一个总结。他认为,从人类认知的角度来看,一个物体应该是下列事物之一:可以触摸或看到的事物;被理解的东西;可以指导思想或行动(想象或采取行动)的东西。有了对象的自然定义,我们就可以用编程语言来描述对象。在不同的编程语言中,设计者也使用各种语言特性来抽象描述对象。最成功的流派是用“类”来描述对象,催生了C++、Java等流行的编程语言。然而,在JavaScript的早期,选择了一种更冷门的方法:原型。这也是我前面说的不合群的原因之一。ipt刚推出时,就被管理层勒令模仿Java。因此,JavaScript创始人BrendanEich在“原型运行时”的基础上引入了new和this等语言特性,使其“看起来更像Java”。在ES6出现之前,大量的JavaScript程序员试图在原型系统的基础上让JavaScript更像基于类的编程,从而产生了很多所谓的“框架”,比如PrototypeJS和Dojo。事实上,它们变成了某种奇怪的JavaScript方言,甚至催生了一系列不兼容的社区,这样做显然得不偿失。如果我们从运行时的角度谈对象,我们就是在谈JavaScript实际运行的模型,因为任何代码执行都必须离不开运行时对象模型,但幸运的是,从运行时的角度来看,你不不必为这些“基于类的设施”所困扰,因为类的概念在任何语言运行时都是弱化的。JavaScript是面向对象的还是基于对象的?首先,让我们看一下JavaScript是如何设计它的对象模型的。JavaScript对象的特性在我看来,不管我们使用什么编程语言,首先应该了解对象的本质特性(参考GrandyBooch《面向对象分析与设计》)。综上所述,该对象具有以下特点。对象是唯一可识别的:即使两个相同的对象也不是同一个对象。对象有状态:对象有状态,同一个对象可能有不同的状态。对象具有行为:也就是说,对象的状态可能会因其行为而改变。我们先来看第一个特征,对象是唯一可识别的。一般来说,各种语言中对象的唯一性都是用内存地址来表示的。因此,JavaScript程序员都知道,任何不同的JavaScript对象都是不相等的。我们可以看到如下代码,o1和o2乍一看是两个一模一样的对象,但是打印出来的结果是false。varo1={a:1};varo2={a:1};console.log(o1==o2);//false关于对象的第二个和第三个特征“状态和行为”,不同的语言用不同的术语来抽象地描述它们,比如C++中的“成员变量”和“成员函数”,以及“属性”和“Java中的方法”。JavaScript是面向对象的还是基于对象的?在JavaScript中,状态和行为被统一抽象为“属性”。考虑到函数在JavaScript中被设计成一个特殊的对象(这个我后面会详细解释,这里就不用赘述了),所以在JavaScript中行为和状态都可以使用属性来抽象。下面这段代码实际上展示了一个普通属性和函数作为属性的例子,其中o是一个对象,d是一个属性,函数f也是一个属性。虽然写法不同,但是对于JavaScript来说,d和f这两个是共同的属性。varo={d:1,f(){console.log(this.d);}};所以,综上所述,在JavaScript中,对象的状态和行为实际上被抽象为属性。如果你用过Java,你一定不会感到惊讶。虽然在设计思想上存在一些差异,但两者都很好地表达了对象的基本特征:身份、状态和行为。在认识到对象的基本特征的基础上,我认为JavaScript中对象的独特之处在于对象是高度动态的,因为JavaScript赋予了用户在运行时为对象添加状态和行为的能力。我举个例子,比如JavaScript允许在运行时为对象添加属性,这与大多数基于类的静态对象设计完全不同。如果你用过Java或者其他语言,你肯定会和我有同感。下面的一段代码显示了如何在运行时向对象添加属性。一开始,我定义了一个对象o。定义完成后,我添加它的属性b。这个操作完全没问题。你必须明白这一点。varo={a:1};o.b=2;console.log(o.a,o.b);//12JavaScript是面向对象的还是基于对象的?为了提高抽象能力,JavaScript的属性被设计成比其他语言更复杂的形式,它提供了两种类型的数据属性和访问器属性(getter/setter)。JavaScript对象的两种属性对于JavaScript来说,属性不仅仅是简单的名称和值。JavaScript使用一组属性来描述特性。先说第一类属性,数据属性。它与其他语言的属性概念比较接近。数据属性有四个特征。value:它是属性的值。writable:判断属性是否可以赋值。enumerable:判断forin是否可以枚举属性。configurable:决定属性是否可以删除或者特征值是否可以改变。大多数情况下,我们只关心数据属性的值。第二类属性是访问器(getter/setter)属性,它也有四个特点。getter:function或undefined,获取属性值时调用。setter:函数或未定义,在设置属性值时调用。enumerable:判断forin是否可以枚举属性。configurable:决定属性是否可以删除或者特征值是否可以改变。访问器属性允许属性在读取和写入代码时执行代码。它可以让用户在写入和读取属性时得到完全不同的值。它可以看作是函数的语法糖。我们平时定义属性的代码都会生成数据属性,其中writable、enumerable、configurable都默认为true。我们可以使用内置函数Object.getOwnPropertyDescripter查看,如下代码所示:varo={a:1};o.b=2;//a和b都是数据属性Object.getOwnPropertyDescriptor(o,"a")//{value:1,writable:true,enumerable:true,configurable:true}Object.getOwnPropertyDescriptor(o,"b")//{value:2,writable:true,enumerable:true,configurable:true这里我们使用两种语法来定义属性。定义属性后,我们使用JavaScriptAPI来查看属性。我们可以发现这样定义的属性都是数据属性,writeable、enumerable、configurable默认值为true。如果我们想改变属性的特性,或者定义accessor属性,我们可以使用Object.defineProperty,例子如下:varo={a:1};Object.defineProperty(o,"b",{value:2,writable:false,enumerable:false,configurable:true});//a和b都是数据属性,只是特征值变了Object.getOwnPropertyDescriptor(o,“A”);//{值:1,可写:真,可枚举:真,可配置:真}Object.getOwnPropertyDescriptor(o,"b");//{value:2,writable:false,enumerable:false,configurable:true}o.b=3;console.log(o.b);//2这里我们使用Object.defineProperty来定义属性,所以定义属性可以改变可写和可枚举的属性。我们还使用Object.getOwnPropertyDescriptor检查发现可写和可枚举的特性确实发生了变化。因为writable特性为false,我们重新给b赋值,b的值不会改变。JavaScript是面向对象的还是基于对象的?在创建对象时,也可以使用get和set关键字来创建访问器属性,代码如下:varo={geta(){return1}};控制台日志(o.a);//1访问与数据属性不同,getter属性会在每次访问该属性时执行getter或setter函数。这里我们的getter函数返回1,所以o.a每次都得到1。这样,我们就明白了,其实一个JavaScript对象的运行时就是一个“属性的集合”。属性使用字符串或Symbols作为键,数据属性或访问器属性的特征值作为值。一个对象就是一个属性的索引结构(索引结构是一种常见的数据结构,我们可以把它理解为一个字典,可以使用键以比较快的速度查找值)。我们以上面的对象o为例,你可以想象“a”是key。这里{writable:true,value:1,configurable:true,enumerable:true}是值。在之前的类型课程中,我们介绍过Symbol类型,它可以使用Symbol作为属性名,这是JavaScript对象的一个??特性。说到这里,如果了解了对象的特性,就不难理解我一开始提出的问题了。你甚至可以理解为什么会有“JavaScript不面向对象”这样的说法:JavaScript的对象设计与目前主流的基于类的面向对象有很大的不同。事实上,虽然这样的对象系统设计比较特殊,但JavaScript提供了完整的运行时对象系统,这使得它可以模仿大多数面向对象的编程范式(下节课将向大家介绍JavaScript中的两种面向对象编程范式:class-basedandprototype-based),所以它也是一门正统的面向对象语言。JavaScript是面向对象的还是基于对象的?JavaScript语言标准也明确指出JavaScript是一种面向对象的语言。我认为标准可以这样说是因为JavaScript高度动态的对象系统。因此,我们应该在理解其设计思想的基础上充分挖掘其能力,而不是生搬硬套其他语言。结语要理解JavaScript对象,必须清空脑海中“基于类的面向对象”知识,回归到对对象的简单认知和面向对象语言独立性的基本理论,才能理解JavaScript面向对象设计训练思想。很多人在思考JavaScript对象的时候,都会用已有的“对象”概念来看问题,最终的结果当然是“断断续续,乱来了”。