首先从构造函数开始。

看如下代码:
function Person() {}
var person = new Person();
person.name = ‘Zxc’;
console.log(person.name) // Zxc
Person是一个构造函数,我们使用了new创建了实列对象person。

什么是prototype

函数才有prototype,每个函数都有prototype
function Person() {
}
// 虽然写在注释里,但是你要注意:
// prototype是函数才会有的属性
Person.prototype.name = ‘Zxc’;
var person1 = new Person();
var person2 = new Person();
console.log(person1.name) // Zxc
console.log(person2.name) // Zxc
Person函数的prototype属性 指向的是一个对象 ,该对象是调用构造函数生成Person函数,
Person函数调用构造函数生成person1,2的实例
prototype1.png

什么是 proto 原型链

每个函数都有自己的prototype,每个实例person都有自己的 _proto
__proto
是实例所特有的。实例 的_proto 指向 函数对象的Prototype
1.JavaScript深入之从原型到原型链 - 图2
综上的代码可以理解为:

  1. function Person() {
  2. }
  3. var person = new Person();
  4. console.log(person.__proto__ == Person.prototype) // true
  5. console.log(Person.prototype.constructor == Person) // true
  6. // 顺便学习一个ES5的方法,可以获得对象的原型
  7. console.log(Object.getPrototypeOf(person) === Person.prototype) // true

实例和原型的关系

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。

  1. function Person() {
  2. }
  3. Person.prototype.name = 'Zxc';
  4. var person = new Person();
  5. person.name = 'Zhu';
  6. console.log(person.name) // Zhu
  7. delete person.name;
  8. console.log(person.name) // Zxc


Person.Prototype的原型是什么?

要理解Person.Prototype 可以理解为是 同时有实例对象和 函数属性的 特殊对象。
它的函数特性体现在它有constructor方法,并可以通过constructorzhiPerson函数
它的实例对象特性,表现在它有proto原型链,指向了它的构造函数的原型。
理解为下图

prototype4.png

原型链

问题又来了 那么Object.prototype的proto指向哪里,

console.log(Object.prototype.proto === null) // true
// null 表示“没有对象”,即该处不应该有值。
可以得出Object.prototype 没有原型。
最终生成的关系图如下,蓝色的箭头线可以理解为原型链
prototype5.png

3点补充

constructor

首先是 constructor 属性,我们看个例子:
function Person() {

}
var person = new Person();
console.log(person.constructor === Person); // true
当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以:
person.constructor === Person.prototype.constructor

proto

其次是 proto ,绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.proto 时,可以理解成返回了 Object.getPrototypeOf(obj)。

真的是继承吗?

最后是关于继承,前面我们讲到“每一个对象都会从原型‘继承’属性”,实际上,继承是一个十分具有迷惑性的说法,引用《你不知道的JavaScript》中的话,就是:
继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。

总结

可以用如下代码解释
console.log(Person.prototype)
image.png
Person.prototy原型实例包含2个属性,constructor,proto,
constructor指向了Person,proto指向Object.prototye,(我们可以发现Object并没有proto属性)
如果不能理解 js内部运行的存储机制,现在的这个理解 感觉就像是玄学。

对象实例化和原型的关系

上面的研究到最后感觉是玄学,所以现在要深入研究对象实例化和原型之间的关系

function new() {
let obj= {}; // 创建的新对象 // 第一个参数是构造函数
let [constructor, …args] = […_arguments
];
// 执行 [[原型]] 连接 ;实际上就是生产了一个新的上下文
obj.proto = constructor.prototype;
// 使用apply在obj作用域中调用构造器函数,属性和方法被添加到 this 引用的对象即obj中
let result = constructor.apply(obj, args);
if (result && (typeof (result) == “object” || typeof (result) == “function”)) {
// 如果构造函数执行的结果返回的是一个对象,那么返回这个对象 return result;
}
// 如果构造函数返回的不是一个对象,返回创建的新对象 return obj;
}