function Persion() {
}
let person1 = new Persion();
__proto__
和prototype
的关系如上图所示,如果明白这两则关系的话这个图是很清晰易见的,看不懂的话可以继续往下看
首先需要明确的一点是,JS中除了一些基本数据类型外其他的都是对象,既然都是对象那么在继承关系或者实例关系中就会存在共享属性(公共区)的概念
在其他语言中存在class类的概念,那么公共区的引用由类来指向没什么问题,但js中没有类的概念(es6后的class本质是语法糖),在js中创建对象是通过构造函数 + new的方式,所以公共区的引用就由构造函数来指代,(注意明确函数也是对象)
而这个指代公共区的引用就是prototype
, 因此prototype
只有在函数对象中拥有,而它所指向的公共区也是一个对象,里面包含constructor, __proto__
两个属性
基于最上面例子来画图大概就是
用代码进行简单模拟为
function Persion() {
}
let o = {
constructor: function() {},
__proto__: xxx
};
Persion.prototype = o;
// 但prototype对象的创建和赋值时自动默认进行的,当一个函数被定义时它就会存在
看到这里可以明确prototype是指向公共区的引用,而我们知道prototype也就是js中的原型概念
然后问题回到了__proto__
,__proto__
又被称为隐式原型,这个属性可以看到在例子中的persion1这个对象实例中和protype指向的对象中都有这个属性,它的作用其实和prototype很像
在函数中通过prototype指向原型(公共区),那么使用函数作为构造函数创造的实例怎么指向这个公共区原型呢?就是使用__proto__
,实例person1通过这个属性指向公共区也就是原型,所以得到
persion1.__proto__ === Persion.prototype
而对于prototype所指对象内部的__proto__
, 会指向上一级的公共区,在本例中就是Object的公共区,由此就会形成原型链继承的逻辑
在继承关系中,对一个实例对象查找某个属性或方法就会沿着原型链查找,就本例而言,
- 1、person1.a会现在person1的实例属性中查询,也就是自身这个对象中查看是个有这个属性
- 2、person1自身属性不存在时,查找persion1.
__proto__
也就是Person的公共区,也是persion1的原型 - 3、还是找不到的话就会根据prototype对象中的
__proto__
指向继续向上查找,形成基于原型链继承关系的层层查找
那么在es6中提出的class语法下的prototype
和__proto__
的指向是怎么样的呢,可以通过以下例子来感受下
class A {
}
// class语法创建类,其默认继承Object
function B() {}
// 构造函数语法创建类,其默认继承Object
let aa = new A();
let bb = new B();
aa.__proto__ === A.prototype; // true
A.prototype.__proto__ === Object.prototype; // true
bb.__proto__ === B.prototype; // true
B.prototype.__proto__ === Object.prototype; // true
// B的例子就是本文中简述的例子应该是显而易见的
// 但A是class,es6中的class属于语法糖
补充:
关于类的 prototype 属性和__proto__属性
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__
属性,指向对应的构造函数的prototype
属性。Class 作为构造函数的语法糖,同时有prototype
属性和__proto__
属性,因此同时存在两条继承链。
(1)子类的__proto__
属性,表示构造函数的继承,总是指向父类。
(2)子类prototype
属性的__proto__
属性,表示方法的继承,总是指向父类的prototype
属性。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
Object.setPrototypeOf(B.prototype, A.prototype);
// 等同于
B.prototype.__proto__ = A.prototype;
Object.setPrototypeOf(B, A);
// 等同于
B.__proto__ = A;