无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向 原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构 造函数。
当把一个函数作为构造函数 (理论上任何函数都可以作为构造函数) 使用new创建对象的时候,那么这个对象就会存在一个默认的不可见的属性,来指向了构造函数的原型对象。 这个不可见的属性__proto__ 没有办法直接访问到。
function Person () {
}
/*
利用构造函数创建一个对象,则这个对象会自动添加一个不可见的属性 __proto__, 而且这个属性
指向了构造函数的原型对象。
*/
var p1 = new Person();
console.log(p1.__proto__ === Person.prototype); //true
原理如图所示
我们能从图中可以看出:
1.尽管p1实例是通过Person new出来的,但是p1实例已经与Person构造函数没有关系了,p1实例指向的是Person.prototype这个原型对象
2.如果我们通过Person创建多个实例对象,这些实例对象都会指向Person的原型对象
3.我们也可以给Person.prototype这个原型对象添加属性和方法,这些属性和方法也会被p1、p2....这些实例对象所继承
4.如果我们访问p1中的一个属性name,在p1实例对象中找到,则返回这个属性。如果p1实例对象中没有找到,则直接去p1实例对象的__proto__属性指向的Person.prototype原型对象中查找,找到则返回。
5.如果p1实例对象添加了一个属性name,则p1实例对象就不会寻找原型对象中的属性name。即在是实例对象中找到就不再往原型对象中寻找。
6.通过p1实例对象只能读取原型对象中的属性name的值,而不能修改原型中的属性name的值。 p1.name = “lisi”; 并不是修改了原型中的值,而是在p1实例对象中给添加了一个属性name。
function Person () {
}
/*
利用构造函数创建一个对象,则这个对象会自动添加一个不可见的属性 __proto__, 而且这个属性
指向了构造函数的原型对象。
*/
var p1 = new Person();
var p2 = new Person();
// console.log(p1.__proto__ === Person.prototype); //true
Person.prototype.name = 'zhangsan';
Person.prototype.age = 20;
console.log(p1.name); //zhangsan
console.log(p2.name); //zhangsan
console.log(p1.name === p2.name); //true
p1.name = 'lisi';
console.log(p1.name); //lisi
console.log(p1.name === p2.name); //false
构造函数中有些方法只能通过构造函数本身调用,实例对象的能够继承原型对象中的方法使用。
例如:
Object.getOwnPropertyDescriptor()
Object.defineProperty()
Object.defineProperties()
就只能通过构造函数调用,不能通过实例对象调用。
下列的方法都是在原型对象中的,可以通过实例对象来进行调用
toString()
toLocaleString()
hasOwnProperty(prop)
propertylsEnumerable(prop)
isPrototypeOf(obj2)