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

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

成都创新互联坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站设计、成都网站建设、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的桓台网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

  • 构造函数
  • 实例
  • 继承
  • 构造函数的属性与方法(私有属性与方法)
  • 实例的属性与方法(共享属性与方法)
  • 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


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


网站题目:再和“面向对象”谈恋爱-对象相关概念(二)
标题URL:http://azwzsj.com/article/pijjei.html