一、什么是原型
1.1 先了解几个概念
1.构造函数
function Dog() {
} //我就是构造函数
var dog = new Dog();
在这里Dog就是一个构造函数,我们使用 new 创建了一个实例对象 dog。
2.原型对象:在声明了一个函数之后,浏览器会自动按照一定的规则创建一个对象,这个对象就叫做原型对象。这个原型对象其实是储存在了内存当中。
3.在声明了一个函数后,这个构造函数(声明了的函数)中会有一个属性prototype,这个属性指向的就是这个构造函数(声明了的函数)对应的原型对象;原型对象中有一个属性constructor,这个属性指向的是这个构造函数(声明了的函数)。下面一张图可以很简单理解:
1.2 使用构造函数创建对象
我们使用 new 创建了一个实例对象 stu。
function Students() {
/* 我就是构造函数 */
}
var stu = new students();
此时,stu就是那个构造函数Students创建出来的对象,这个stu对象中是没有prototype属性的,prototype属性只有在构造函数students中有。
可以看出,构造函数Students中有prototype属性,指向的是Students对应的原型对象;而stu是构造函数Students创建出来的对象,他不存在prototype属性,所以在调用prototype的时候的结构是undefined,但stu有一个__proto__
属性,stu调用这个属性可以直接访问到构造函数Students的原型对象(也就是说,stu的 __proto__
属性指向的是构造函数的原型对象),如图。
说明:
1.创建stu对象虽然使用的是Students构造函数,但是对象创建出来之后,这个stu对象其实已经与Students构造函数没有任何关系了。stu对象的__proto__
属性指向的是Students构造函数的原型对象。
2.如果使用new Students()创建多个对象stu1、stu2、stu3,则多个对象都会同时指向students构造函数的原型对象。
3.我们可以手动给这个原型对象添加属性和方法,那么stu1,stu2,stu3…这些对象就会共享这些在原型中添加的属性和方法。
4.如果我们访问stu中的一个属性name,如果在stu对象中找到,则直接返回。如果stu对象中没有找到,则直接去stu对象的__proto__
属性指向的原型对象中查找,如果查找到则返回。(如果原型中也没有找到,则继续向上找原型的原型—原型链)。
5.如果通过stu对象添加了一个属性name,则stu对象来说就屏蔽了原型中的属性name。 换句话说:在stu中就没有办法访问到原型的属性name了。
6.通过stu对象只能读取原型中的属性name的值,而不能修改原型中的属性name的值。 stu.name = “李四”; 并不是修改了原型中的值,而是在stu对象中给添加了一个属性name。
1.3 prototype
每个函数都有一个 prototype 属性,他指向了这个构造函数的原型对象。
1.4__proto__
这是每一个JavaScript对象(除了 null )都具有的一个属性,叫__proto__
,这个属性会指向该对象的原型。
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
2 原型链
1.查找属性,如果本身没有,则会去__proto__
中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有proto,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined.
2.p.__proto__.constructor == function Person(){}
3.p.___proto__.__proto__== Object.prototype
4.p.___proto__.__proto__.__proto__== Object.prototype.__proto__ == null
5.通过__proto__
形成原型链而非protrotype