资讯专栏INFORMATION COLUMN

再和“面向对象”谈恋爱 - 对象相关概念(二)

qpwoeiru96 / 476人阅读

摘要:所有的对象都是由构造函数创建的对象哪来的构造函数生的。而普通函数不能生成对象不孕不育,构造函数可以生成对象有生育能力。别急,记住那句话永远指向实例对象对应的构造函数的,那就先看实例对象是谁。

上一篇文章把对象的概念讲解了一下,这篇文章要重点解释最让大家犯迷糊的一些概念,包括

构造函数

实例

继承

构造函数的属性与方法(私有属性与方法)

实例的属性与方法(共享属性与方法)

prototype(原型)

__proto__(原型)

构造函数
构造函数依然是个函数,用来生成对象。所有的对象都是由构造函数创建的

对象哪来的?构造函数生的。而普通函数不能生成对象(不孕不育),构造函数可以生成对象(有生育能力)。每个对象都会有一个自己对应的构造函数,但是不代表每个构造函数都会生(生成实例),有不会生的构造函数,例如Math对象(不孕不育)

console.dir(Array);     //数组的构造函数
console.dir(Function); //函数的构造函数
console.dir(Object);   //对象的构造函数

new Math();     //报错,不能生成实例
new Window();   //报错,不能生成实例

唐僧说过一句话,用来解释构造函数与实例再合适不过了“人是人他妈生的,妖是妖他妈生的”那这里的人他妈与妖他妈就是构造函数。人与妖其实是下面要说的概念“实例”

实例
实例就是对象,由构造函数生成

平时用的实际的东西都是实例(获取的DOM元素、声明的数组、声明的函数、声明的对象),有时候需要new关键字生成(不是绝对的)

实例身上有一个constructor属性,它指向对应构造函数(生母)

判断一个对象是否是某个构造函数的实例,用instanceof运算符

//实例
[];             //不用new生成
new Array();    //用new生成

[].constructor===Array; //true
[] instanceof Array;    //true
继承
生成的实例具有构造函数身上的属性与方法(就像老鼠的儿子会打洞)
一个对象身上有另一个对象身上的属性或方法,这种具有的方式就叫继承

说到继承首先想到的就是遗产。老人去世了,在二环里留了一套四合院,那这套房子归谁呀?不可能归我吧,归我的话我就不用在这吭哧吭哧写文章了。那也不能归你吧,归你了你也不能座这看我吭哧吭哧写的文章。得归人家儿子,这个就是继承。那从这个例子中得出几个重要信息,继承者与被继承者之间是有一定关系的,最简单的就是父子或者母子关系。

那回到程序中,构造函数就是老人,实例就是儿子。那实例的属性或者方法哪来的?构造函数的,不过它可以继承过来,这是合理合法的。但是也会有特殊情况,假如老人膝下无子,那这房子怎么办?充公?不能吧,要你你干么?不过这个老人有一个唯一的亲人,就是弟弟。那弟弟能否拿到这个房子呢,应该是可以的。再回到程序中,刚才这种情况其实程序中也存在,就是一个对象可以有另一个对象身上的东西。

//arr身上是并没有push方法,这个方法来自于构造函数Array,是arr继承了构造函数身上的这个方法
const arr=[1,2];
arr.push(3);
console.log(arr);    //[1, 2, 3]

//Array对象身上并没有valueOf方法,这个方法来自于Object对象,是Array对象继承了Object对象的这个方法
Array.valueOf===Object.valueOf;   //true
构造函数的属性与方法(私有属性与私有方法)
构造函数身上的属性与方法,只有构造函数能用,实例不能用

再回到这1个亿的例子中来,这个老人为啥有个四合院呢,这位老人原来是个老兵,立过一等功,战功赫赫,获得了无数勋章。那我问你他的这些荣誉,儿子有么?或者弟弟有么?没有吧。这个就是构造函数特有的属性与方法,实例身上是没有的

//构造函数的私有属性与方法
console.log(Array.name);    //Array
console.log(Array.of(5));    //[5]

//实例不能用
const arr=[];   //实例
console.log(arr.name);        //undefined
arr.of(6);    //报错
实例的属性与方法(共享属性与共享方法)
实例身上的属性与方法,只有实例能用,构造函数不能用(放在prototype里)

老人有两个孩子,这俩孩子跟我学会了js,又学会了ES6,现在在百度上班做开发,这你说扯不扯,我都快编不下去了。那这些技能老人会么,他不会。所以这个就叫实例的属性与方法,只能实例去用,构造函数用不了

//实例的方法
const arr=[1,2,3];
console.log(arr.concat(["a","b"]));    //[1, 2, 3, "a", "b"]

Array.concat(["a","b"]));    //报错。你的就是你的,我的就是我的,不能互用。就跟媳妇是一样

//但构造函数可以间接用
console.log(Array.prototype.concat(["a","b"]));    //["a", "b"]
prototype(原型)
1、构造函数身上的一个属性,它的类型为对象,这个属性的值就是原型
2、这个对象里放的属性与方法,就是构造函数的共享属性与共享方法,所有实例都能用

还得回到那一个亿的例子里,我们说儿子能继承老子的遗产,为什么呢?因为他有个神器叫户口本,国家给发的,能证明他们是父子关系。你去办手续的时候肯定要拿着户口本。现在都得要证件,当然有的时候你可能需要到派出所开个证明,证明你就是你。虽然有点扯哈,但是有真实发生过,如果派出所不给你证明,那你就不是你。回到程序中,虽然你知道实例是构造函数生的,那实例就能有构造函数身上的方法,为什么呢?其实他们也有证,跟户口本一样,这个证就是prototype

//prototype 原型
console.log(Array.prototype);   //数组的原型对象

//如果把原型上的方法删除了,那实例就不能用了。证明原型里放的属性与方法都是实例的属性与方法
const arr=[];
Array.prototype.push=null;
arr.push(6);    //报错
__proto__(原型)
1、这个属性是浏览器自己部署的,到了ES6也没有正式写入标准里,建议大家不要用它,用Object.getPrototypeOf()方法替代
2、它也是指原型对象,与prototype一样。但是有区别:归属不同,prototype是函数身上的属性,__proto__是对象身上的属性

这个属性与prototype属性往往让大部分人都百思不得其解,看得是一头雾水,脑袋拧成了麻花,网上的资料是一堆一堆,但往往大家看得是一愣一愣。其实这俩东西很简单,是一道推算题,首先,你要明白原型的结果只有一个,就是构造函数的prototype属性的值。那为什么浏览器看热闹不嫌事大,给我们找麻烦,又部署了一个__proto__呢?其实浏览器也是好心,但没成想办了坏事。在没有这个属性以前,只能从函数身上找到原型。为了能从实例对象身上也能找到原型,浏览器就部署了这个属性。以字符串为例推算如下:

字符串构造函数的原型放在String.prototype里。那现在我能否通过实例找到这个原型呢?也就是str ? ===String.prototype

const str=new String("kaivon");
console.dir(str);    //打印出实例,点开后看到__proto__

要通过实例找到原型的话,首先要通过实例找到构造函数(因为原型在构造函数身上)。前面有说过,实例身上都有一个属性叫constructor,这个就指向构造函数

console.log(str.constructor===String);    //true 通过实例找到了构造函数

那找到了构造函数,原型不就放在构造函数身上么?所以变成了这样

console.log(str.constructor.prototype===String.prototype);    //true

到这里就推出来如何通过实例找到原型,但是这么写是不是有点长呢?天空一声巨响,__proto__闪亮登场,浏览器为了简化操作,就让__proto__等于constructor.prototype,也就是变成下面这样

console.log(__proto__===constructor.prototype);    //true

所以整个语句其实就可以变成这样

console.log(str.__proto__===String.prototype);    //true

到这里就明白了吧,再总结一下

prototype属性是构造函数身上的,指向原型

__proto__属性是对象身上的,指向原型

实例的.__proto__===构造函数.prototype

现在是不是恍然大悟、如梦初醒、豁然开朗,但是我不得不浇你一头冷水啊,还没有完,看下面

console.dir(String);//打印出构造函数,点开也看到了__proto__

刚才不是说这玩意是实例身上的么,现在怎么跑到构造函数身上了?童话里都是骗人的吗?别急,构造函数是函数么?函数是对象么?是吧,这就对了。其实上面的推理题都好说,都简单。大家弄不懂的是在这。网上的文章就在这里让你的麻花越拧越紧。构造函数是函数,是函数它就是对象,__proto__既然是对象身上的,那这个构造函数身上就一定会有。点开__proto__看一下

里面的内容不应该是String原型的内容么?好像不是哎,这就傻逼了,刚搞清楚的东西现在全乱了。别急,记住那句话__proto__永远指向实例对象对应的构造函数的prototype,那就先看实例对象是谁。我们点开的这个String它是什么?它是构造函数,它的类型是个函数。虽然它是一个构造函数,但在这里它就是一个实例对象,并且它的类型是函数,所以它是Function构造函数的实例,那Function的实例对象身上的__proto__不应该指向Functionprototype么?所以这里面的内容为Function对象的原型

console.log(String.__proto__===Function.prototype);    //true

接着看,这里还有一个__proto__

真是害怕什么来什么呀!这是__proto__来自于Functionprototype,它是个原型对象,原型对象的数据类型当然为对象了,所以它是Object构造函数的实例,那Object的实例对象身上的__proto__不应该指向Objectprototype么,所以点开这个__proto__里面的内容是Object.prototype的值

console.log(String.__proto__.__proto__===Object.prototype);    //true

因为Object已经是顶层对象了,所以在它的prototype里不会再出现__proto__了,但是有人说Object其实还有继承,继承于null。但是我不太认同这种说法,__proto__的值是个对象类型数据,而Object已经是顶层对象了,它原型对象的__proto__肯定没有值了,在ECMAScriptnull的数据类型又为对象,所以就呼应上了,而不是继承于null

Object.prototype.__proto__===null;  //true

到这里我把面向对象当中关键的一些概念算是说清楚了,下一篇文章来说一下真正的面向对象概念!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96630.html

相关文章

  • 再和面向对象恋爱 - 图片预加载组件(七)

    摘要:源码下载至此再和面向对象谈恋爱系列文章已经全部更新完毕写文章不易,且行且珍惜 再和面向对象谈恋爱 - 对象简介(一)再和面向对象谈恋爱 - 对象相关概念(二)再和面向对象谈恋爱 - 面向对象编程概念(三)再和面向对象谈恋爱 - class(四)再和面向对象谈恋爱 - 继承(五)再和面向对象谈恋爱 - super(六) 通过前面的六篇文章已经把ES6的面向对象跟大伙说清楚了,大家最关心的...

    caoym 评论0 收藏0
  • 再和面向对象恋爱 - 面向对象编程概念(三)

    摘要:说到底面向对象才是程序语言的根本。其实面向对象编程说的就是自定义对象。里并没有类的概念,所以严格上来讲这是个假的面向对象里的面向对象编程现在好了,终于听到别人鄙视我们了,给我们提供了类这个概念,其实是向传统语言更靠齐了。 通过前两篇文章,我们了解了对象的概念以及面向对象里的相关概念等知识,那前面说了对象分类里的前两种,这篇文章要详细去说第三种自定义对象,那真正的好戏这就来了! 面向对象...

    Cruise_Chan 评论0 收藏0
  • 再和面向对象恋爱 - class(四)

    摘要:在上一篇文章里我介绍了一下面向对象编程的概念,在最后终于喜出望外看到了提供了类的概念了。而到了里面真正的类与构造函数现在是分离的,通过上面的代码可以看出来,这种写法正是面向对象的正统写法。 在上一篇文章里我介绍了一下面向对象编程的概念,在最后终于喜出望外看到了ES6提供了类的概念了。那这个类如何去用,是这篇文章的主题。ES6给我们提供了一个class关键字。这个关键字跟以前的var l...

    刘东 评论0 收藏0
  • 再和面向对象恋爱 - 继承(五)

    摘要:面向对象里最大的特点应该就属继承了。在第二篇文章里说过原型实例跟构造函数之间的继承,并且还讲了一道推算题。 通过上一篇文章想必各位老铁已经熟悉了class了,这篇文章接着介绍继承。面向对象里最大的特点应该就属继承了。一个项目可能需要不断的迭代、完善、升级。那每一次的更新你是要重新写呢,还是在原有的基础上改吧改吧呢?当然,不是缺心眼的人肯定都会在原来的基础上改吧改吧,那这个改吧改吧就需要...

    Airmusic 评论0 收藏0
  • 再和面向对象恋爱 - super(六)

    摘要:同时弹出的结果是指向了子类,又说明虽然调用的是父类的构造函数,但是调用完后会指向子类,指向也被改成了子类的实例。 在上一篇文章里介绍了继承,那其中说过一个很关键的东西想要继承子类里里必需先调用一个super方法。而super的作用绝对是价值连城!同时super的作用还有多种,并且跟你的使用环境有关系。 1、当作函数使用 super被当作函数使用,这种情况是最普遍的,上一篇文章里已经使用...

    Towers 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<