手机版

JavaScript面向对象编程(困惑的兰姆)

时间:2021-08-31 来源:互联网 编辑:宝哥软件园 浏览:

导言:

前面的系列文章基本讲解了JavaScript核心知识点的基本语法和标准库;本章开始进入JavaScript核心知识的高级部分,——面向对象编程,将进一步了解对象的数据类型,并描述几种创建对象的设计模式以及JavaScript独有的继承机制;

1.理解对象和面向对象的程序设计

1.1 面向对象的程序设计

‘面向对象编程’(OOP)本身就是一种编程的思维模式,它把世界上的一切都看作是对象的集合,世界的运行是对象之间分工协作的结果,体现了一切都是“对象”的思想;在编程中,面向对象编程可以看作是编写具有特定功能的对象(模块)并有机地协同工作,即目前的模块化编程是面向对象编程的实际应用;

1.2 理解对象

对象在之前的系列文章中已经提到过。从数据特征来看,对象是无序属性(键值对)的集合;我们可以使用文字和构造函数来创建最简单的对象:

var person=new Object();person.name=' terenperson.age=18岁;person . greet=function(){ console . log(' hello ' this . name);}var teren={ name:'teren ',age:18,greet : function(){ console . log(' hello ' this . name);}}通常,简单的对象是以文字的方式创建的;上述对象是真实对象的抽象表达;

1.3 对象的属性类型

在前一章中,我们可以使用delete命令删除对象的一些属性,但有些不能。我们只能使用Object.keys()方法遍历可枚举的属性,那么对象的属性是否有一些我们还没有理解的特性呢?ES5提供了一个只在内部用于描述对象各种属性的属性,由[[attribute]]表示,不能在JavaScript中直接访问。我们非常熟悉的一个栗子是Number构造函数构造的实例对象;

我们无法访问num。[[PrimitiveValue]]直接,这个属性只能通过num.valueOf()访问;

ES5定义了对象属性的两个特征,数据特征和访问器特征,对象属性可以同时具备这两个特征;

数据特征定义了对象属性值的特征。属性值可以包括以下四个数据特征:

[[值]]:存储属性值;[[可写]]:是否可写;[[可枚举]]:是可枚举属性;[[可配置]]:是否可以用删除命令删除;访问器属性定义了在访问和设置属性时由对象的属性调用的两个函数:getter和setter;

[[Get]]:访问属性时调用的函数;[[Set]]:设置属性时调用的函数;

以下两个特征由示例对象直接解释:

//数据特征;var teren={ };Object.defineproperty (teren,{value:' teren ',可写: false,可枚举3360 true,可配置: true })//访问器属性;//html div id=' name '/div//jsvar obj=object . definepreoperty({ },' name ',{ set : function(name){ document . getelementbyid(' name ')。innerHTML=name},get : function(){ console . log(document . getelementbyid(' name ')。innerHTML ) },})obj . name=' hello world ' obj . name[demo]

Object.defineProperties可以一次配置一个对象的多个属性;

2. 创建对象的方式

在上一节中,我们对面向对象的编程思想和对象有了初步的了解。在这一节中,我们深入讨论了对象的创建方法及其优缺点;

不同的创建对象的方式也可以简称为设计模式,不同的设计模式在实际的编程应用中发挥着不同的作用;

2.1 单例模式

singleton pattern是唯一一个产生类的实例对象,这样可以保证你只有一个可以实际使用的对象实例;

在单例模式下,创建如下对象:

var singleton={ attr:1,method : function(){ return this . attr } } var ex1=singleton;var ex2=singletonex1===ex2//真上述创建单个实例的方式:

优点:使用非常简单;缺点:缺少封装,暴露成员,初始化时占用资源;

您可以使用闭包来解决这个问题:

var实质=(function(){ var唯一;函数init(){ var类型;返回{ settype : function(t){ return type=t;} } }返回{ getInstance:function(){ if(!unique){ unique=init();}返回唯一;} }})();var Adam=substance . getinstance();var Eve=substance . getinstance();亚当===eveadam . settype(' Man ')//Man

2.2 工厂模式

singleton模式只能创建单个实例对象,也就是说,如果将实例对象赋予多个变量,就会出现对象引用问题,即修改一个变量会影响另一个变量;有时候我们需要创建多个结构相似的对象,只有一些属性不同。这时,工厂模式派上了用场;

工厂模式的设计思路是可以像工厂一样批量生产属性和方法相似的对象,可以解决很多相似的问题,比如创建多个弹出窗口(只能用不同的标题);

函数人(姓名、年龄){ var obj=new Object();obj.name=name年龄=年龄;obj.greet=function(){返回' hello ' this.name};return obj}var Adam=person('Adam ',18);var Eve=人(' Eve ',20);以上出厂模式:

优点:可以批量生产结构相似的物体;封装创建对象的细节;缺点:对象的类型,是哪个构造函数创建的,无法解决;

2.3 构造函数模式

构造函数可以创建特定类型的对象,而像Array和RegExp这样的原生对象可以创建特定类型的实例对象。

函数Person(姓名、年龄){ this.name=namethis.age=年龄;this.greet=function(){返回' hello ' this.name}}var p1=new Person('Adam ',18);var p2=新人(' Eve ',20);使用构造函数模式可以解决谁创建实例对象的问题;

上述代码与工厂模式的区别在于:

1.不显示创建新对象;2.将属性和方法直接分配给此对象;3.没有退货单;4.将函数名的开头大写,以区别普通函数;5.使用新运算符创建对象实例;

新算子原理

使用新运算符调用函数不同于直接调用函数。使用新运算符运行函数的过程如下:

创建新对象;将构造函数的范围分配给新对象,并执行构造函数中的代码;返回一个新对象;使用代码表示如下:

函数Person(姓名、年龄){ this.name=namethis.age=年龄;this.greet=function(){返回' hello ' this.name} }函数createPerson(姓名、年龄){ var o=new Object();人称称呼(o,姓名,年龄);返回o;}var p1=createPerson('Adam ',18);var p2=createPerson('Eve ',20);使用构造函数模式创建对象的优点和缺点是:

优点:可以识别对象所属的构造函数;缺点:如果有不同实例对象共享的属性和方法,使用构造函数模式会浪费内存;

【注意】关于这个关键词的更多知识,请参考【这是什么】;如果没有被新的操作符调用,构造函数和普通函数是一样的;

2.4 原型模式

每个函数都有一个原型属性,可以部署特定类型实例共享的属性和方法;

function Person(){ } } Person . prototype . greet=function(){ return ' hello ' this . name;在Person函数的prototype属性上部署原始的greet函数,这样p1和p2就可以共享这个方法,不像构造函数模式那样,通过为每个创建的实例添加一个greet方法来浪费内存;

【注意】关于原型对象的更多理解,请参考下一节——JavaScript继承机制;

使用原型模式创建对象的优点和缺点是:

优点:可以很好地实现每个实例的共享属性和方法;缺点:单独使用原型模式不会区分不同实例的私有属性;

2.5 混合模式

混合模式是综合构造器模式和原型模式的优缺点,构造器模式部署实例的私有属性和原型模式部署实例的公共属性;

函数Person(姓名、年龄){ this.name=namethis.age=年龄;} person . prototype . greet=function(){ return ' hello ' this . name;}var p1=new Person('Adam ',18);var p2=新人(' Eve ',20);混合模式是用于创建自定义类型(类)的最广泛使用和高度认可的设计模式。

【注】当然,设计模式不仅仅是上面提到的,还更深刻。请参考《设计模式》这本书和《设计模式梗概》之前小羊写的一篇文章;

3.JavaScript的继承机制

在上一节中,我们通过创建不同的对象模式,隐式引入了原型对象的概念。在这一节中,我们将更多地了解原型对象、原型链及其继承机制;

前面我们知道,从数据特征来看,对象是无序属性(键值对)的集合;现在,从面向对象的角度来看,任何对象都是更抽象的对象的实例,可以理解为类的概念;从这个角度,我们可以重新定义对象和类的含义:对象可以说是现实事物的抽象,对象封装属性和方法,属性值指的是对象的状态,方法指的是对象的行为;类是提供模板的“对象”,模板是对象的抽象;举个简单的栗子:

函数Person(姓名、年龄){ this.name=namethis.age=年龄;} person . prototype . greet=function(){ return ' hello ' this . name;}var p1=new Person('Adam ',18);var p2=新人(' Eve ',20);上面的代码显示p1和p2是亚当和夏娃的抽象,“类”Person是两个实例的抽象,为创建类似结构的人提供了标准模板。【注】ES6之前,JavaScript中没有类,ES6中定义了类;

3.1 原型对象

在上一节的原型模式中,我们提到每个函数都有一个原型属性,它指向函数的原型对象,可以部署特定类型的实例共享的属性和方法;

随着对原型对象理解的加深,原型对象不仅可以部署特定类型实例共享的属性和方法,也是实现JavaScript继承的关键。

只要创建一个新的函数,就会为该函数创建一个原型属性,每个原型属性都会自动获得一个构造函数属性,该属性指向原型属性所在的函数指针;

当构造函数用于创建实例时,该实例包含指向构造函数原型对象的内部属性__proto__。

于是,一种简单的继承产生了;以下面的代码为例:

函数Person(姓名、年龄){ this.name=namethis.age=年龄;} person . prototype . greet=function(){ return ' hello ' this . name;}var p1=new Person('Adam ',18);var p2=新人(' Eve ',20);

创建构造函数后,自动创建原型属性,原型对象下的默认构造函数属性指向原型属性所在的函数Persongreet方法部署在原型对象上,实例p1的内部属性__proto__指向构造函数Person.prototype,从而继承了构造函数原型对象上的greet方法。

[注意]

实例的_ _ prototype _ _指向构造函数的原型对象实现继承,存在于实例和构造函数的原型对象之间,但不存在于构造函数之间;当js引擎解析一个对象的属性时,它将首先搜索对象本身的属性,如果没有,它将搜索__proto__指向的原型对象上的属性,直到找到为止。如果由对象本身定义的属性与原型对象上的属性具有相同的属性名,那么当读取属性时,它自己的属性将屏蔽原型对象上的属性。函数Person(姓名、年龄){ this.name=namethis.age=年龄;} person . prototype . greet=function(){ return ' hello ' this . name;}var p1=new Person('Adam ',18);P1 . greet()//hello Adam;P1 . greet=function(){ return ' hello world ' }

修改构造器的原型对象可以直接使用点操作,具有直接给原原型对象添加属性的效果。有时候需要添加的属性太多,点操作太麻烦,可以采用重置原型对象的方法:函数人(姓名、年龄){this。name=namethis.age=年龄;} person . prototype={ constructor : person,greet1:function(){},greet2:function(){},greet 3: function(){ };var p1=新人(' Adam ',18);Person.prototype.constructor.name//'Object'要注意的是,重置原型对象后,原型对象的构造函数的属性需要回参考Person构造函数;如果没有重置构造函数,那么Person.prototype就是由文字量创建的对象,文字量创建的对象的默认构造函数是Object;

3.2 原型链

以上,我们只定义了一个构造函数实现一个继承;如果有多个构造函数,并且它们之间存在继承关系,就会形成一个关于继承的原型链。

function SuperType(姓名、年龄){ this.name=namethis . age=age } SuperType . prototype . greet=function(){ return ' hello ' this . name } function SubType(姓名、年龄、身高){ SuperType.call(this、姓名、年龄);this.height=高度;} subtype . prototype=object . create(SuperType . prototype);SubType . prototype . constructor=SubType;subtype . prototype . method=function(){ return 1;} var instance=new subtype ('teren ',18,180)以上是实现多类间继承最常用的设计模式;使用object . create(supertype . prototype)的优点和缺点是:

优点:您可以创建一个新的SuperType.prototype对象并将其分配给SuperType . prototype,并修改SuperType . prototype,而不会影响原始构造函数SuperType。原型;缺点:子类和父类的原型值虽然相同,但内存不同,从而切断了子类和父类之间的类型;

也可以使用SubType.prototype=new SuperType()来达到同样的效果,有优点也有缺点:

优点:可以反映子类和父类之间的继承关系;缺点:子类有父类的私有属性;

因此,Object.create()方法通常在原型链实际实现时使用,而新的SuperType()方法在理解原型链时使用。

3.3 与原型对象相关的方法

遍历对象属性方法Object.keys()和Object.getOwnPropertyNames()用于遍历对象本身而不是继承的属性名称并返回一个数组,其中Object.keys()只返回可枚举的属性;

In用于检查对象是否具有属性。它不区分属性是对象自己的属性还是继承的属性;为.in用于遍历对象的所有可枚举属性(无论它是自己的还是继承的)

如果只遍历自己的属性,可以使用以下代码:

For(实例中的var键){if(实例。hasownproperty (key)) {console。log (key)}}是判断一个属性是否是自己的方法

对象。prototype.hasownproperty()返回一个布尔值,该值用于确定属性是在对象本身上定义的,还是在原型链上定义的。

设置和获取实例对象的原型对象的方法

Object.getPropertyOf()返回实例对象的原型对象;对象。setpropertyof (obj,prototype)可以传递两个参数,第一个是现有参数,第二个是prototype对象;

判断一个对象是否是另一个对象的原型对象

对象;原型;isprototypeof()用于判断一个对象是否是另一个对象的原型;

摘要

通过阅读这篇文章,我们可以知道:

面向对象编程是一种思维模式,它把世界上的一切都看作是对象的集合,世界的运行是对象之间分工协作的结果;映射到程序设计,就是把每个具有特定功能的对象(模块)编写出来,有机地集成起来,使程序工作起来;从数据特征来看,对象是无序键值对的集合,对象的属性有两个特征:——数据特征和访问器特征;创建对象的不同方式可以称为设计模式。简要说明了单例模式、工厂模式、构造模式、原型模式、混合模式等。从面向对象的角度来看,对象可以说是现实事物的抽象,而类是对象的抽象。每个函数都有一个原型对象原型,不仅可以部署特定类型实例的共享属性和方法,也是JavaScript继承的关键;Prototype Prototype对象有一个构造函数属性,指向prototype的函数指针;每当使用构造函数创建实例时,实例内部都包含指向构造函数原型对象的内部属性__proto__从而实现简单继承;当一个构造函数是b构造函数的实例时,会形成一个原型链,即a构造函数的实例对象c的__proto__指向a构造函数的原型对象,a构造函数的__proto__指向b构造函数的原型对象。b构造函数原型__proto__指向函数构造函数原型,函数原型__proto__指向对象原型;与原型对象相关的方法包括:对象.键()和对象. getPropertyNames(),用于.在方法中,Object.getPrototypeOf()和Object.setPrototypeOf()方法,object . prototypeof . hasown property()和object . prototypeof . isprototypeof()方法;参考材料

《JavaScript高级程序设计(第3版)》 《JavaScript标准参考教程》 ——阮一峰

版权声明:JavaScript面向对象编程(困惑的兰姆)是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。