上一篇文章把对象的概念讲解了一下,这篇文章要重点解释最让你们犯迷糊的一些概念,包括javascript
- 构造函数
- 实例
- 继承
- 构造函数的属性与方法(私有属性与方法)
- 实例的属性与方法(共享属性与方法)
- prototype(原型)
- __proto__(原型)
构造函数依然是个函数,用来生成对象。全部的对象都是由构造函数建立的
对象哪来的?构造函数生的。而普通函数不能生成对象(不孕不育),构造函数能够生成对象(有生育能力)。每一个对象都会有一个本身对应的构造函数,可是不表明每一个构造函数都会生(生成实例),有不会生的构造函数,例如Math对象(不孕不育)java
console.dir(Array); //数组的构造函数 console.dir(Function); //函数的构造函数 console.dir(Object); //对象的构造函数 new Math(); //报错,不能生成实例 new Window(); //报错,不能生成实例
唐僧说过一句话,用来解释构造函数与实例再合适不过了“人是人他妈生的,妖是妖他妈生的”那这里的人他妈与妖他妈就是构造函数。人与妖实际上是下面要说的概念“实例”segmentfault
实例就是对象,由构造函数生成
平时用的实际的东西都是实例(获取的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个亿的例子中来,这个老人为啥有个四合院呢,这位老人原来是个老兵,立过一等功,战功赫赫,得到了无数勋章。那我问你他的这些荣誉,儿子有么?或者弟弟有么?没有吧。这个就是构造函数特有的属性与方法,实例身上是没有的spa
//构造函数的私有属性与方法 console.log(Array.name); //Array console.log(Array.of(5)); //[5] //实例不能用 const arr=[]; //实例 console.log(arr.name); //undefined arr.of(6); //报错
实例身上的属性与方法,只有实例能用,构造函数不能用(放在prototype里)
老人有两个孩子,这俩孩子跟我学会了js,又学会了ES6,如今在百度上班作开发,这你说扯不扯,我都快编不下去了。那这些技能老人会么,他不会。因此这个就叫实例的属性与方法,只能实例去用,构造函数用不了prototype
//实例的方法 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
code
//prototype 原型 console.log(Array.prototype); //数组的原型对象 //若是把原型上的方法删除了,那实例就不能用了。证实原型里放的属性与方法都是实例的属性与方法 const arr=[]; Array.prototype.push=null; arr.push(6); //报错
一、这个属性是浏览器本身部署的,到了ES6也没有正式写入标准里,建议你们不要用它,用Object.getPrototypeOf()
方法替代
二、它也是指原型对象,与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__
不该该指向Function
的prototype
么?因此这里面的内容为Function
对象的原型
console.log(String.__proto__===Function.prototype); //true
接着看,这里还有一个__proto__
真是惧怕什么来什么呀!这是__proto__
来自于Function
的prototype
,它是个原型对象,原型对象的数据类型固然为对象了,因此它是Object
构造函数的实例,那Object
的实例对象身上的__proto__
不该该指向Object
的prototype
么,因此点开这个__proto__
里面的内容是Object.prototype
的值
console.log(String.__proto__.__proto__===Object.prototype); //true
由于Object
已是顶层对象了,因此在它的prototype
里不会再出现__proto__
了,可是有人说Object
其实还有继承,继承于null
。可是我不太认同这种说法,__proto__
的值是个对象类型数据,而Object
已是顶层对象了,它原型对象的__proto__
确定没有值了,在ECMAScript
中null
的数据类型又为对象,因此就呼应上了,而不是继承于null
。
Object.prototype.__proto__===null; //true
到这里我把面向对象当中关键的一些概念算是说清楚了,下一篇文章来讲一下真正的面向对象概念!