前端工程师-我这样理解constructor、prototype、_proto_
正文前餐
如果大家正在学习原型、原型链、构造函数,一定搜到了很多文章,并且大多数都提到了阮老师的Javascript继承机制的设计思想这一片神作,所以我也在这里提一嘴,因为我也是反复看了这一篇文章,还有大家的思路后,自己又进行了一个角度的理解,同时我还推荐这三篇,娓娓道来,慢慢吸收
prototype起源
为了没有首先看阮老师文章的童鞋,我就把阮老师的一些东西搬过来方便大家理解
有一个人亲切的教过我,想研究一个技术或者是其他的东西,就要知道他是为了解决什么问题而产生的
而javascript最初就是为了让我们的浏览器可以与网页互动产生互动而急切被生产出来的一种脚本语言
最初我们的网页是只能浏览的:下面这个图的样子:
Brendan Eich 这位大神就被赋予了这个艰巨的工作
正好当时java,c++( 面向对象的语言)语言非常火热,所以这位大神就被影响到了,了解过一段时间的js的大家也注意到了Javascript里面所有的数据类型都是对象(object),就连Function他也是一个函数对象类型的数据。
面向对象的语言有三大特点 中有一个是继承(封装、继承、多态),那js到底要不要实现继承呐,这个就是个问题了
如果有继承,那js这个脚本语言岂不是就要跟高级语言差不多了,如果没有,那实在是不方便,比如,我们新声明一个对象,
var obj = {}; //就是有了继承,我们可以使用 obj.toString () // 如果没有继承 自己实现实现一下toString() //或者,我们用Object.assign() 或者还有其他方法,但这样岂不是浪费内存吗
并且给prototype起了一个亲切的名字叫原型,只要在原型上的方法或者属性都可以被所有实例继承,不用继承的就会放在构造函数中
并且实例对象一旦创建,js就自动帮你把prototype对象引用好了,那他是把这个对象放在了那里呢?
细说prototype
首先:我用浏览器敲出来给大家看看一段代码
// 代码均在最新的edge中验证的,在此附上,可以敲着玩 var test = function(){}; test.prototype let temp1 = new test(); temp1.prototype
大家在学习prototype时,是不是也可能有敲过上面的代码呀,我是敲了无数遍了
就这张图,我来说说我看到的几个问题:
1.test是一个构造函数,然后我们可以直接访问到他的原型test.prototype
2.temp1是test的实例,访问原型的时候确是undefined。
3.还有刚才提出的疑问,prototype对象放在了那里呢?
1.test是一个构造函数,然后我们可以直接访问到他的原型test.prototype

2.temp1是test的实例,访问原型的时候确是undefined
解答:首先我们看一下Object.prototype的官方解释,Object.prototype确实是Object的原型对象
https://developer.mozilla.org/zh-CN/docs/conflicting/Web/JavaScript/Reference/Global_Objects/Object
大家再看,所有的js对象都是Object的实例,而我们prototype是Object的私有方法,也就是说,我们的实例对象是拿不到的
大概是这个样子:这就是我们普通对象取不到原型的原因
此处附上代码
// 比如,我们的ObjectDemo是我们创建对象的工厂函数 function ObjectDemo(){ this.protoType = {} } ObjectDemo.prototype._proto_ = {};// 这里我们一会说明 // 我们不论用任何写法来创建对象,都是ObjectDemo的实例 var obj = new ObjectDemo(); console.log(obj.prototype); // undefined console.log(obj._proto_ ); // {}
那原型去哪里了?
被指向在这里,ObjectDemo.prototype._proto_ = {}; 那我们就明白了,_proto_就指向了对象的原型,那岂不是非常可怕,整个原型链被修改掉了
我相信讨论到这里基本上大家不会再迷糊,为什么原型有时候是prototype,有时候是_proto_ ;
总结一下:
1.prototype就是指一个对象或函数对象的原型,是为了实现继承而出现的一种解决方式。
2.基本对象是不能直接访问prototype的,他其实是Object的私有属性,一般对象的原型是被指向了_proto_
但是由于这个属性太过疯狂,所以已经被Web 标准中删除,即使浏览器目前还在支持,但是以后应该也不会支持了,目前更推荐Object.getPrototypeOf
/Reflect.getPrototypeOf
和Object.setPrototypeOf
/Reflect.setPrototypeOf
constructor
prototype是为了实现继承,那constructor又是为了解决什么问题呢?请看一下Javascript 面向对象编程(一):封装
之前我们一直都在说,实例实例,那你怎么知道这个对象是哪一个构造函数生出来的宝宝呢?constructor就是为了解决这个问题的,他就是放在实例中,告诉你你的爸爸是谁
(其实不论你是直接写构造函数,再去new出实例,还是直接写出一个对象,其实他们都是Object的实例,因为new运算之后返回的还是一个对象,说到底,就是在操作对象)
我们来看看constructor:还是附上一段代码

附上代码:
function ObjectDemo(){ this.abc = {}; } ObjectDemo.prototype.a ='123'; ObjectDemo.prototype.b ='234'; var obj = new ObjectDemo(); console.log("ObjectDemo.constructor:"+ObjectDemo.constructor); console.log("ObjectDemo.prototype.constructor:"+ObjectDemo.prototype.constructor); console.log("obj.constructor:"+obj.constructor);
// 输出
ObjectDemo.constructor:function Function() { [native code] }
ObjectDemo.prototype.constructor:function ObjectDemo(){ this.abc = {};}
obj.constructor:function ObjectDemo(){this.abc = {};}
既然说,constructor是为了说明当前的实例,他的父亲是哪位,那我们就来套一套解释一下。
ObjectDemo是一个构造函数,也就是一个函数对象,函数对象, 只要是对象,他都能通过原型链获取到原型上的方法,所以我们一定可以拿到constructor属性,因为他在prototype上
ObjectDemo.constructor:function Function() { [native code] } 就可以说明,ObjectDemo是Function的一个实例
再看后面两个,我们就发现
这里就是js帮我们处理好的部分,也就是当前实例的爸爸到底是谁
多说一句:obj.__proto__ === ObjectDemo.prototype 同时,这个地方也是js帮我们写好地方了,这里就可以看到可怕之处
obj.__proto__.a = '123' //"123" ObjectDemo.prototype.a // "123" // 继承将会错乱
总结一下:
constructor属性,是在原型上的,他就是为了解决继承时,说明你的父亲是谁而产生的一种解决方案
总结
prototype与constructor都是为了实现js继承时对不同问题的解决方案
prototype:解决实例对构造函数的方法与变量的继承问题
constructor:说明实例与构造函数的关系
一般对象中,需要用到__proto__
只是此对象已经被web标准删除,以后也可能不再支持,因此我们需要用到Object.getPrototypeOf()这个方法
附录
此处是对面向对象理解:(仅供参考)
面向对象:把构成问题事务分解成各个对象,分析这些对象在整个解决问题步骤中的行为,对这些对象进行封装抽象,最后通过使用这些对象的能力,达到解决问题的目的。
欢迎继续阅读此系列