详解面向对象、构造函数、原型与原型链

详解面向对象、构造函数、原型与原型链

 

为了帮助大家能够更加直观的学习和了解面向对象,我会用尽量简单易懂的描述来展示面向对象的相关知识。并且也准备了一些实用的例子帮助大家更加快速的掌握面向对象的真谛。

  • jQuery的面向对象实现
  • 封装拖拽
  • 简易版运动框架封装

这可能会花一点时间,但是却值得期待。所以如果有兴趣的朋友可以来简书和公众号关注我。

而这篇文章主要来聊一聊关于面向对象的一些重要的基本功。

一、对象的定义

在ECMAScript-262中,对象被定义为“无序属性的集合,其属性可以包含基本值,对象或者函数”

也就是说,在JavaScript中,对象无非就是由一些列无序的key-value对组成。其中value可以是基本值,对象或者函数。

创建对象

我们可以通过new的方式创建一个对象。

也可以通过对象字面量的形式创建一个简单的对象。

当我们想要给我们创建的简单对象添加方法时,可以这样表示。

访问对象的属性和方法

假如我们有一个简单的对象如下:

当我们想要访问他的name属性时,可以用如下两种方式访问。

如果我们想要访问的属性名是一个变量时,常常会使用第二种方式。例如我们要同时访问person的name与age,可以这样写:

 

这种方式一定要重视,记住它以后在我们处理复杂数据的时候会有很大的帮助。

二、工厂模式

使用上面的方式创建对象很简单,但是在很多时候并不能满足我们的需求。就以person对象为例。假如我们在实际开发中,不仅仅需要一个名字叫做TOM的person对象,同时还需要另外一个名为Jake的person对象,虽然他们有很多相似之处,但是我们不得不重复写两次。

很显然这并不是合理的方式,当相似对象太多时,大家都会崩溃掉。

我们可以使用工厂模式的方式解决这个问题。顾名思义,工厂模式就是我们提供一个模子,然后通过这个模子复制出我们需要的对象。我们需要多少个,就复制多少个。

相信上面的代码并不难理解,也不用把工厂模式看得太过高大上。很显然,工厂模式帮助我们解决了重复代码上的麻烦,让我们可以写很少的代码,就能够创建很多个person对象。但是这里还有两个麻烦,需要我们注意。

第一个麻烦就是这样处理,我们没有办法识别对象实例的类型。使用instanceof可以识别对象的类型,如下例子:

因此在工厂模式的基础上,我们需要使用构造函数的方式来解决这个麻烦。

三、构造函数

在JavaScript中,new关键字可以让一个函数变得与众不同。通过下面的例子,我们来一探new关键字的神奇之处。

为了能够直观的感受他们不同,建议大家动手实践观察一下。很显然,使用new之后,函数内部发生了一些变化,让this指向改变。那么new关键字到底做了什么事情呢。嗯,其实我之前在文章里用文字大概表达了一下new到底干了什么,但是一些同学好奇心很足,总期望用代码实现一下,我就大概以我的理解来表达一下吧。

 

JavaScript内部再通过其他的一些特殊处理,将var p1 = New(Person, 'tom', 20); 等效于var p1 = new Person('tom', 20);。就是我们认识的new关键字了。具体怎么处理的,我也不知道,别刨根问底了,一直回答下去太难 – -!

老实讲,你可能很难在其他地方看到有如此明确的告诉你new关键字到底对构造函数干了什么的文章了。理解了这段代码,你对JavaScript的理解又比别人深刻了一分,所以,一本正经厚颜无耻求个赞可好?

当然,很多朋友由于对于前面几篇文章的知识理解不够到位,会对new的实现表示非常困惑。但是老实讲,如果你读了我的前面几篇文章,一定会对这里new的实现有似曾相识的感觉。而且我这里已经尽力做了详细的注解,剩下的只能靠你自己了。

但是只要你花点时间,理解了他的原理,那么困扰了无数人的构造函数中this到底指向谁就变得非常简单了。

所以,为了能够判断实例与对象的关系,我们就使用构造函数来搞定。

关于构造函数,如果你暂时不能够理解new的具体实现,就先记住下面这几个结论吧。

  • 与普通函数相比,构造函数并没有任何特别的地方,首字母大写只是我们约定的小规定,用于区分普通函数;
  • new关键字让构造函数具有了与普通函数不同的许多特点,而new的过程中,执行了如下过程:
    1. 声明一个中间对象;
    2. 将该中间对象的原型指向构造函数的原型;
    3. 将构造函数的this,指向该中间对象;
    4. 返回该中间对象,即返回实例对象。

四、原型

虽然构造函数解决了判断实例类型的问题,但是,说到底,还是一个对象的复制过程。跟工厂模式颇有相似之处。也就是说,当我们声明了100个person对象,那么就有100个getName方法被重新生成。

这里的每一个getName方法实现的功能其实是一模一样的,但是由于分别属于不同的实例,就不得不一直不停的为getName分配空间。这就是工厂模式存在的第二个麻烦。

显然这是不合理的。我们期望的是,既然都是实现同一个功能,那么能不能就让每一个实例对象都访问同一个方法?

当然能,这就是原型对象要帮我们解决的问题了。

我们创建的每一个函数,都可以有一个prototype属性,该属性指向一个对象。这个对象,就是我们这里说的原型。

当我们在创建对象时,可以根据自己的需求,选择性的将一些属性和方法通过prototype属性,挂载在原型对象上。而每一个new出来的实例,都有一个__proto__属性,该属性指向构造函数的原型对象,通过这个属性,让实例对象也能够访问原型对象上的方法。因此,当所有的实例都能够通过__proto__访问到原型对象时,原型对象的方法与属性就变成了共有方法与属性。

我们通过一个简单的例子与图示,来了解构造函数,实例与原型三者之间的关系。

由于每个函数都可以是构造函数,每个对象都可以是原型对象,因此如果在理解原型之初就想的太多太复杂的话,反而会阻碍你的理解,这里我们要学会先简化它们。就单纯的剖析这三者的关系。

 

 

 

图示

通过图示我们可以看出,构造函数的prototype与所有实例对象的__proto__都指向原型对象。而原型对象的constructor指向构造函数。

除此之外,还可以从图中看出,实例对象实际上对前面我们所说的中间对象的复制,而中间对象中的属性与方法都在构造函数中添加。于是根据构造函数与原型的特性,我们就可以将在构造函数中,通过this声明的属性与方法称为私有变量与方法,它们被当前被某一个实例对象所独有。而通过原型声明的属性与方法,我们可以称之为共有属性与方法,它们可以被所有的实例对象访问。

当我们访问实例对象中的属性或者方法时,会优先访问实例对象自身的属性和方法。

在这个例子中,我们同时在原型与构造函数中都声明了一个getName函数,运行代码的结果表示原型中的访问并没有被访问。

我们还可以通过in来判断,一个对象是否拥有某一个属性/方法,无论是该属性/方法存在与实例对象还是原型对象。

in的这种特性最常用的场景之一,就是判断当前页面是否在移动端打开。

更简单的原型写法

根据前面例子的写法,如果我们要在原型上添加更多的方法,可以这样写:

除此之外,我还可以使用更为简单的写法。

这种字面量的写法看上去简单很多,但是有一个需要特别注意的地方。Person.prototype = {}实际上是重新创建了一个{}对象并赋值给Person.prototype,这里的{}并不是最初的那个原型对象。因此它里面并不包含constructor属性。为了保证正确性,我们必须在新创建的{}对象中显示的设置constructor的指向。即上面的constructor: Person

四、原型链

原型对象其实也是普通的对象。几乎所有的对象都可能是原型对象,也可能是实例对象,而且还可以同时是原型对象与实例对象。这样的一个对象,正是构成原型链的一个节点。因此理解了原型,那么原型链并不是一个多么复杂的概念。

我们知道所有的函数都有一个叫做toString的方法。那么这个方法到底是在哪里的呢?

先随意声明一个函数:

那么我们可以用如下的图来表示这个函数的原型链。

 

原型链

其中foo是Function对象的实例。而Function的原型对象同时又是Object的实例。这样就构成了一条原型链。原型链的访问,其实跟作用域链有很大的相似之处,他们都是一次单向的查找过程。因此实例对象能够通过原型链,访问到处于原型链上对象的所有属性与方法。这也是foo最终能够访问到处于Object原型对象上的toString方法的原因。

基于原型链的特性,我们可以很轻松的实现继承

五、继承

我们常常结合构造函数与原型来创建一个对象。因为构造函数与原型的不同特性,分别解决了我们不同的困扰。因此当我们想要实现继承时,就必须得根据构造函数与原型的不同而采取不同的策略。

我们声明一个Person对象,该对象将作为父级,而子级cPerson将要继承Person的所有属性与方法。

首先我们来看构造函数的继承。在上面我们已经理解了构造函数的本质,它其实是在new内部实现的一个复制过程。而我们在继承时想要的,就是想父级构造函数中的操作在子级的构造函数中重现一遍即可。我们可以通过call方法来达到目的。

而原型的继承,则只需要将子级的原型对象设置为父级的一个实例,加入到原型链中即可。

 

 

原型链

当然关于继承还有更好的方式,这里就不做深入介绍了,以后有机会再详细解读吧。

六、总结

关于面向对象的基础知识大概就是这些了。我从最简单的创建一个对象开始,解释了为什么我们需要构造函数与原型,理解了这其中的细节,有助于我们在实际开发中灵活的组织自己的对象。因为我们并不是所有的场景都会使用构造函数或者原型来创建对象,也许我们需要的对象并不会声明多个实例,或者不用区分对象的类型,那么我们就可以选择更简单的方式。

我们还需要关注构造函数与原型的各自特性,有助于我们在创建对象时准确的判断我们的属性与方法到底是放在构造函数中还是放在原型中。如果没有理解清楚,这会给我们在实际开发中造成非常大的困扰。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279014.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何将Wii遥控器用作陀螺仪鼠标

If you have a spare Nintendo Wii remote with the Motion Plus add-on, you can use it to control your Windows PC from across the room. Here’s how to get it working in a couple of easy steps. 如果您有带Motion Plus附加组件的备用Nintendo Wii遥控器,则…

php 自带 web server 如何重写 rewrite .htaccess

为什么80%的码农都做不了架构师&#xff1f;>>> <?php // filename: route.php if (preg_match(/\.(?:png|jpg|jpeg|gif|css|js)$/, $_SERVER["REQUEST_URI"])) {return false; } else {include __DIR__ . /index.php; } 更好的写法&#xff1a; &l…

sci-hub谷歌插件_Google Home Hub具有隐藏屏幕设置菜单

sci-hub谷歌插件You can adjust the brightness or set an alarm on your Google Home Hub with a voice command. But if you’re trying to be quiet or there’s a lot of background noise, you can also do these things using a hidden Screen Settings menu. 您可以使用…

二叉树的前序、中序、后序遍历与创建

#include <iostream> #include <string> #include <stack> using namespace std; struct node; typedef node *pNode; struct node { char data; pNode left, right; }; string line; string::iterator it; // 前序扩展序列建立二叉树 void plan…

flex-2

1、 2、 justify&#xff1a;整理版面 3、 4、归纳 justify-content&#xff1a;flex-start&#xff08;默认&#xff09;、center、flex-end 下面还会提到剩下的两种项目在主轴上对齐方式&#xff1a; space-between:两端对齐&#xff08;项目间距离相等&#xff09; space-ar…

火狐标签在中间_在Firefox中保留未使用的标签

火狐标签在中间If you have a lot of content heavy webpages open in Firefox, it soon adds up on memory usage. The BarTab extension puts unused tabs on hold and keeps them unloaded until you are ready to access them. 如果您在Firefox中打开了大量内容繁重的网页&…

iphone手机备忘录迁移_如何在iPhone和iPad上使用语音备忘录

iphone手机备忘录迁移Whether you’re recording a voice message as a reminder of that million dollar idea or catching a snippet of a new song you know you’ll forget, the iPhone and iPad’s Voice Memos app is the perfect tool. 无论您是录制语音消息来提醒这一百…

php 执行文件tar打包,利用tar for windows对大量文件进行快速打包

近期将某些网站换服务器&#xff0c;由于网站数量巨大&#xff0c;加上附件和静态页&#xff0c;文件数量异常多&#xff0c;考虑先打包然后直接传过去。起初尝试用winrar打包&#xff0c;但是发现即使选择”仅储存”速度仍然慢到无法接受&#xff0c;后来想到了tar&#xff0c…

DDD~领域事件中使用分布式事务

对于一个聚合来说&#xff0c;它可能会被附加很多事件&#xff0c;这里我们叫它领域事务&#xff0c;因为一个聚会我们可以把它理解成一个领域&#xff0c;一个业务。对于领域事件不清楚的同学可以看看我的这篇文章《DDD~领域事件与事件总线》&#xff0c;里面有详细的说明&…

如何在PowerPoint中制作打字机或命令行动画

Adding quirky animations to your Microsoft PowerPoint presentation gives your slideshow a little extra life. Not only will adding a typewriter or command line animation entertain your audience, but it will also keep them focused on the text. 在您的Microsof…

canvas 平滑运动_什么是电视上的运动平滑?人们为什么讨厌它?

canvas 平滑运动Willy Barton/Shutterstock.com威利巴顿/Shutterstock.comIf you’ve just bought a new TV, you might be wondering why everything you watch feels eerily sped up and smooth, like you’re watching a live broadcast all the time. You’re not imaginin…

linux guard什么进程,使用linux系统性能监控工具KSysguard监控远端主机介绍

KDE System Guard默认的窗口前端图形界面使用传感器(sensors)获得要显示的信息。传感器返回的可以是一个简单的数值或更复杂的信息如表格。针对不同的信息类型都提供了一个或多个显示界面。这些显示界面被组织在多个工作表中&#xff0c;工作表可以独立存储和加载。KSysguard主…

macbook充电_如何判断MacBook是否正在充电

macbook充电2p2play / Shutterstock2p2play / ShutterstockForgetting to charge your MacBook properly overnight can leave you with a headache in the morning. And if you’re troubleshooting a broken MacBook, checking if it’s able to charge is one way to rule o…

chrome同步_如何在Chrome中打开或关闭同步

chrome同步Google Chrome lets you sync up your Google account to your browser across any device. When enabled, bookmarks, history, passwords, extensions, and themes—among many other settings—sync from your Google account, creating a seamless experience no…

linux系统输入指令,详解linux系统输入输出管理和vim的常用功能

####系统中输入输出的管理####1.理解系统的输入输出重定向输入重定向是指把文件导入到命令中&#xff0c;而输出重定向则是把原本要输出到屏幕的数据信息写入到指定文件中。2.管理输入输出的符号##输出重定向> ##重定向正确输2> ##重定向错误输出&> …

Deep Learning(深度学习)学习笔记整理(二)

本文整理了网上几位大牛的博客&#xff0c;详细地讲解了CNN的基础结构与核心思想&#xff0c;欢迎交流 [1]Deep learning简介 [2]Deep Learning训练过程 [3]Deep Learning模型之&#xff1a;CNN卷积神经网络推导和实现 [4]Deep Learning模型之&#xff1a;CNN的反向求导及练习 …

百度新闻 谷歌新闻_每日新闻摘要:到目前为止,Google I / O提供的最佳信息

百度新闻 谷歌新闻Google’s yearly developer conference started yesterday, and the keynote was chock-full of announcements, demos, and some utterly mind-blowing tech. From Assistant to Android, here’s some of the best stuff to come out of I/O 2019 so far. …

word2016 语法检查_如何改进Microsoft Word的语法检查器

word2016 语法检查Microsoft Word comes with a powerful grammar checker, but many of its advanced grammar detection features are disabled by default. Grammarly is popular, but you don’t need it to add grammar checking to Word. Word itself contains a free al…

linux服务器硬件监控,Linux服务器实时监控加载硬件信息

Linux服务器监控之实时监控加载硬件信息Linux负有盛名的特点之一是其非凡的稳定性。然而&#xff0c;如果您的硬件有缺陷或配置不正确&#xff0c;即使是世界上最稳定的操作系统也不会对您有什么帮助。计算机系统是由软件系统硬件系统组成的&#xff0c;检测硬件状态对于保障整…

Using Python with Oracle

2019独角兽企业重金招聘Python工程师标准>>> Using Python with Oracle This page discusses using Python with Oracle. The page is based on the cx_oracle Python extension module. It was developed on a VM running Oracle Enterprise Linux 6U4 runnng Orac…