在开始以前要明确一点,“在JS里,万物皆对象”,方法(Function)是对象,方法的原型(Function.prototype)也是对象。所以,它们都会具备对象共有的特色。chrome
1、prototype和__proto__分别是什么?函数
prototype(显式原型)是对象的一个属性(每一个对象都有一个prototype属性),这个属性是一个指针,指向一个对象,经过它能够向对象添加属性和方法。this
__proto__(隐式原型)是对象的一个内置属性,是JS内部使用寻找原型链的属性,也就是说,当咱们访问obj这个对象中的x属性时,若是在obj中找不到,那么就会沿着__proto__依次向上查找。spa
注意:用chrome和FF均可以访问到对象的__proto__属性,IE不能够。因此咱们建议避免使用__proto__,由于它存在兼容性问题。prototype
两者关系:一个对象的隐式原型指向构造该对象的构造函数的原型指针
2、建立对象实例的new作了些什么?code
var Person = function(){}; Person.prototype.sayName = function() { alert("My Name is Jacky"); }; Person.prototype.age = 27; var p = new Person();
console.log(p.age); // 27 p.sayName(); // My Name is Jacky
这段代码你们都能看懂,可是有人思考过这个问题吗:咱们并无显式设置p这个实例的age属性,为何p.age能够打印出27?是什么帮咱们悄悄作了处理?答案就是__proto__。对象
当执行 var p = new Person() 时 ,悄然发生了如下事情:blog
var p={};
p.__proto__ = Person.prototype; // 一个对象的隐式原型指向构造该对象的构造函数的原型
Person.call(p);
为了证实关键的第二步代码的正确性,咱们执行:原型链
alert(p.__proto__ === Person.prototype); // true
有这层等价关系存在,就能够保证明例化对象p能够找到原型链上的属性。
p是一个引用指向Person的实例化对象,咱们在Person的原型上定义了一个sayName方法和age属性,当咱们执行p.age时,会先在this的内部查找(也就是构造函数内部),若是没有找到,就沿着原型链向上追溯。
这里的向上追溯是怎么向上的呢?就是经过__proto__属性来连接到原型(也就是Person.prototype)进行查找。最终在原型上找到了age属性。
3、一个更复杂的示例
var Person = function() {}; Person.prototype.say = function() { console.log("Person say"); } Person.prototype.salary = 50000; var Programmer = function() {}; Programmer.prototype = new Person(); Programmer.prototype.writeCode = function() { console.log("Programmer writes code"); }; Programmer.prototype.salary = 500; var p = new Programmer(); p.say(); // Person say
p.writeCode(); // Programmer writes code
console.log(p.salary); // 500
你们能够先本身思考一下,在new Programmer()时悄然发生了什么?
------------------------------------思考分割线------------------------------------------
在var p = new Programmer()时发生了如下事情:
var p = {}; p.__proto__ = Programmer.prototype; p.__proto__ = new Person(); p.__proto__.__proto__ = Pserson.prototype; Person.call(p.__proto__); Programmer.call(p);
当咱们调用 p.say() 时,p 中是没有 say 属性,因而到 p 的 __proto__ 属性中去找,也就是 Programmer.prototype,此时 Programmer.prototype 是等于 new Person(),但 new Person() 中也没有 say 属性,因而又到 new Person().__proto__ 中找,此时 new Person().__proto__ 等于 Pserson.prototype 的,咱们定义了 Person.prototype.say=function(){}; 因此,p 在 Person.prototype 中就找到了这个方法。
4、一张粗糙的理解图
1.构造函数Foo()
构造函数的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,全部构造函数声明的实例(这里是f1,f2)均可以共享这个方法。
2.原型对象Foo.prototype
Foo.prototype保存着实例共享的方法,有一个指针constructor指回构造函数。
3.实例
f1和f2是Foo这个对象的两个实例,这两个对象也有属性__proto__,指向构造函数的原型对象,这样就能够访问原型对象的全部方法。
另外:
构造函数Foo()除了是方法,也是对象(万物皆对象),它也有__proto__属性,指向谁呢?指向它的构造函数的原型对象,而函数的构造函数是Function,所以Foo.__proto__ = Function.prototype。
Function(), Object()也是同样的道理。Function是由Object构造出来的,因此Function的__proto__指向了Object.prototype
最后,Object.prototype的__proto__属性指向null。