JavaScript中__proto__ 和prototype的区别
转载请注明 作者:源码先生, 文章链接:https://www.debugself.com/2018/03/08/js_proto/, 请勿用于商业用途
JavaScript不区分类和实例的概念,js中只有实例,不存在类似C++中的类的概念;而JS是面向对象的,没有类的概念如何实现面向对象(如继承的功能)呢?JS是通过原型来实现面向对象编程,这里的原型,就大约等于C++/Java中类的概念
__proto__:大约等于C++/Java中类的概念,下面使用Object.create()新建对象,来说明__proto__的含义
Object.create()参数传入一个实例Student,返回一个新对象xiaoming,而新对象xiaoming的原型就是Student
1 | // 原型对象: |
上述代码中,xiaoming的原型是Student,xiaohong的原型也是Student,当我们给原型Student新增Student.run2()函数后,xiaoming,xiaohong都自动拥有run2函数
1 | Student.run2 = function(){ |
prototype:专门为构造函数才引入的属性;下面**使用构造函数来新建对象,说明prototype的含义**
1 | function Student(name) { |
你会问,咦,这不是一个普通函数吗?
这确实是一个普通函数,但是在JavaScript中,可以用关键字new来调用这个函数,并返回一个对象:
1 | var xiaoming = new Student('小明'); |
注意:
如果不写new,这就是一个普通函数,它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;。
这里使用构造函数来创建新的对象,而没有使用Object.create(),造成xiaoming.__proto__ != Student,可以通过下面的代码来验证
1 | Student.run2 = function(){ |
上面我们为Student添加了run2函数,但是xiaoming和xiaohong并不能调用run2函数,所以xiaoming.__proto__ != Student
但是,我们却有以下事实:
- xiaoming.__proto__ === xiaohong.__proto__;即xiaoming和xiaohong拥有相同的原型
- xiaoming和xiaohong都是由构造函数Student构造而来
既然xiaoming和xiaohong来自Student,那么xiaoming和xiaohong的原型肯定和Student有关系,但是xiaoming和xiaohong的原型又不等于Student,那么该如何表示xiaoming和xiaohong的原型呢?
个人根据事实倒推,估计js的原型被创造时,为了表示xiaoming和xiaohong共同的原型,就为Student引入了prototype这个属性,用Student.prototype表示xiaoming和xiaohong的原型,即
xiaoming.__proto__ === xiaohong.__proto__ === Student.prototype
请注意,在JS中,只有函数对象,即typeof(obj) == function的对象才有prototype属性,而其他类型的object是没有prototype属性的,但是所有类型的object都有__proto__属性的,这也可以印证上述的推测,即prototype是专门为构造函数才引入的属性,用来表示由该构造函数,构造出来的全部对象,所共有的那个原型
JavaScript中__proto__ 和prototype的区别,总结以下
__proto__ 是所有对象都有的属性,obj.__proto__指向了obj的原型,注意是指向了原型,而不是原型本身;那么obj的原型本身是什么呢,有以下两种情况:
- obj = Object.create(Student);此时obj的原型本身是Student
- obj = new Student(‘小明’);此时obj的原型本身是Student.prototype
在上图中“某个对象”,就是xiaohong.__proto__,也即Student.prototype,通过读写Student.prototype来修改原型;新增Student.prototype.run2()函数后,而xiaoming就拥有了run2函数
通过prototype实现继承
如何通过原型链实现继承呢,比如从Student我想扩展出PrimaryStudent,这是需要使用到prototype了,代码稍微复杂,请参考https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014344997013405abfb7f0e1904a04ba6898a384b1e925000
通过class和extends实现继承:比prototype更简洁
后来ES6直接引入class和extends,这样不需要手动通过protype,而是直接通过class和extends就可以直接扩展出PrimaryStudent,当然,class和extends内部依旧是通过prototype实现的