每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特定引用类型的实例所共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处是在它上面定义的属性和方法可以被共享。如下所示:

function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function() { 
    console.log(this.name);
};

let person1 = new Person(); 
person1.sayName(); // "Nicholas"
let person2 = new Person(); 
person2.sayName(); // "Nicholas"
console.log(person1.sayName == person2.sayName); // true

这里所有的属性和方法都添加到了Person的prototype属性上,构造函数体中什么也没有,但这样定义后,调用该构造函数所创建的对象仍然拥有相应的属性和方法。
在任何时间,创建一个函数就会按照一定的规则为这个函数创建一个prototype属性(指向原型对象)。默认情况下,所有原型对象自动获得一个constructor的属性,指回与之关联的构造函数。对上面的例子,Person.prototype.constructor指向Person。在自定义构造函数时,原型对象默认只会获得constructor属性。其他所有方法均继承自Object。每次调用构造函数创建一个新的实例,这个实例的内部([Prototype])指针就会被赋值为构造函数的原型对象。关键点在于:实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有。可以用一张图来表示实例,构造函数与原型对象的关系:
图片说明
上图展示了 Person 构造函数、Person 的原型对象和 Person 现有两个实例之间的关系。注意, Person.prototype 指向原型对象,而 Person.prototype.contructor 指回 Person 构造函数。原 型对象包含 constructor 属性和其他后来添加的属性。Person 的两个实例 person1 和 person2 都只 有一个内部属性指回Person.prototype,而且两者都与构造函数没有直接联系。另外要注意,虽然这两 个实例都没有属性和方法,但person1.sayName()可以正常调用。这是由于对象属性查找机制的原因。
此外Object还有两个重要的方法,getPrototypeOf()返回参数内部特性[[Prototype]]的值。使用它可以轻松的获取一个对象的原型;setPrototype()可以向实例的私有特性[[Prototype]]写入一个新值,但使用 Object.setPrototypeOf()可能造成的性能下降,可以通过 Object.create()来创 建一个新对象,同时为其指定原型。
显式原型与隐式原型
1,每个函数function都有一个显式原型prototype
2,每个实例对象都有一个隐式原型proto
3,对象的隐式原型值为其对应构造函数的显式原型的值