原型
prototype 属性
在JavaScript中,每个函数创建时都有一个prototype属性
// 构造函数
function Person (){}
var person = new Person()
console.log(Person.prototype);
// 普通函数
function fn(){}
console.log(fn.prototype);
如上代码,构造函数Person和普通函数fn都具有prototype属性
[[Prototype]] 和 __proto__
JavaScript的对象中都包含了一个[[Prototype]]内部属性(除了null),这个属性所指向的就是该对象的原型。
[[Prototype]]作为对象的内部属性,是不能被直接访问的,从 ECMAScript 6 开始,[[Prototype]]可以通过Object.getPrototypeOf()和Object.setPrototypeOf()访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性__proto__。
function Person (){}
var person = new Person()
console.log(person.__proto__ === Person.prototype); // true
如上代码,实例对象person的__proto__属性等于构造函数Person的原型
实例与构造函数原型的关系:
constructor
在原型对象中,还包含一个constructor属性,这个属性对应创建所有指向该原型的实例的构造函数
function Person (){}
var person = new Person()
console.log(person.__proto__ === Person.prototype); // true
console.log(person.__proto__.constructor === Person); // true
实例对象、构造函数和构造函数原型之间的关系:
原型的原型
Person.prototype 是原型对象,既然是对象它也有[[Prototype]]这个属性,也就是有它的原型对象。
function Person (){}
var person = new Person()
console.log(Person.prototype.__proto__);
这里我们打印了 Person.prototype.__proto__,发现 constructor指向Object()构造函数。说明Person.prototype的原型等于Object.prototype
同时这个原型对象里有个属性__proto__等于null,表示Object.prototype 它就是原型的顶端了。
所以:
Person.prototype.__proto__.constructor === Object // true
Person.prototype.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null // null
现在我们的关系图再次更新一下:
函数原型
从上面的关系图发现,构造函数为什么有[[Prototype]]属性呢?
因为每个 JavaScript 函数实际上都是一个 Function 对象。就是函数是Function()构造函数的实例对象
function Person() {}
function fn() {}
console.log(fn.constructor === Function) // true
console.log(Person.constructor === Function) // true
console.log(Person.__proto__ === Function.prototype) // true
console.log(Function.prototype.constructor === Function) // true
console.log(Function.prototype.__proto__ === Object.prototype) // true
如上代码,Person()、Object()和Function()构造函数都是Function的实例对象,所以它们__proto__指向Function.prototype。
Function.prototype.constructor又指向了构造函数本身Function()
Function.prototype的__proto__指向了原型对象Object.prototype
现在我们可以得出一些结论:
- 每个函数创建时都有一个
prototype属性 - 每个对象中都包含了一个
[[Prototype]]内部属性(除了null),这个属性所指向的就是该对象的原型。 - 因为函数是
Function()构造函数的实例对象,所以有[[Prototype]]属性 - 在原型对象中,还包含一个
constructor属性,这个属性对应创建所有指向该原型的实例的构造函数 - 在原型链中,原型链顶端是
Object.prototype Function.prototype继承Object.prototype而产生- 一切对象继承自
Object.prototype,一切函数对象继承自Function.prototype
原型链
当我们访问一个对象属性时,如果在这个对象上找不到,就会从它的__proto__属性找到它的原型对象,如果原型对象上也没找到,就继续如此查找,直到找到终点 Object.prototype,这个就是原型链
function Person(){}
Person.prototype.name = 'jack'
Person.prototype.sayName = function(){
console.log(this.name);
}
var p1 = new Person()
p1.sayName() // jack
p1.name = 'alice'
p1.sayName() // alice
delete p1.name
p1.sayName() // jack
console.log(p1.toString()); // [object Object]
以上代码执行过程:第一次指向sayName()方法,
- 执行
sayName()方法,实例对象p1不存在name属性,在构造函数Person.prototype上找到name属性,输出 jack - 给实例对象
p1添加属性name,此时输出 alice - 删除了实例对象的
name属性,在实例上找不到name又找到Person.prototype上了,输出 jack - 调用
toString()方法,实例对象上和Person.prototype上都不存在,继续找,在Object.prototype上面找到后执行