一、原型的概述:
我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。
逻辑上可以这么理解:prototype 通过调用构造函数而创建的那个对象的原型对象。
使用原型的好处可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。
二、使用原型创建对象
function Box() {} //声明一个构造函数,函数体内什么都没有,如果有叫做实例属性,实例方法 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; /*比较一下原型内的方法地址是否一致:*/ var box1 = new Box(); var box2 = new Box(); alert(box1.run == box2.run); //true,方法的引用地址保持一致
为了更进一步了解构造函数的声明方式和原型模式的声明方式,我们通过图示来了解一下:
在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的。
__proto__属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性 constructor。通过这两个属性,就可以访问到原型里的属性和方法了。
IE 浏览器在脚本访问__proto__会不能识别,火狐和谷歌浏览器及其他某些浏览器均能识别。虽然可以输出,但无法获取内部信息。
function Box() {} //声明一个构造函数 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; var box1 = new Box(); var box2 = new Box(); alert(box1.prototype);//这个属性是一个对象,访问不到 alert(box1.__proto__); //这个属性是一个指针指向prototype原型对象, 打印结果是[object Object] 在IE中结果是undefined /* constructor是构造函数的属性,获取构造函数本身 作用是被原型指针定位,然后等到构造函数本身,其实就是对象实例对应的原型对象 */ alert(box1.constructor); alert(box1.age);//可以直接访问原型对象中的属性和方法,因为底层会自动调用prototype和__proto__和constructor等属性
判断一个对象是否指向了该构造函数的原型对象(即判断一个对象实例是不是指向了对象的原型对象)可以使用 isPrototypeOf()方法来测试。
function Box() {} //声明一个构造函数 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; var box1 = new Box(); var box2 = new Box(); alert(box1.age);//结果打印出age的值 alert(Box.prototype.isPrototypeOf(box1)); //只要实例化对象,即都会指向
三、原型模式的执行流程
1、先查找构造函数实例里的属性或方法,如果有,立刻返回;
2、如果构造函数实例里没有,则去它的原型对象里找,如果有,就返回;
虽然我们可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。
function Box() {} //声明一个构造函数 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; var box1 = new Box(); alert(box1.name); //Lee,原型里的值 box1.name = ‘Jack‘; alert(box1.name); //Jack,就近原则, var box2 = new Box(); alert(box2.name); //Lee,原型里的值,没有被 box1 修改
如果想要 box1 也能在后面继续访问到原型里的值,可以把构造函数里的属性删除即可,具体如下:
function Box() {} //声明一个构造函数 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; var box1 = new Box(); alert(box1.name); //Lee,原型里的值 box1.name = ‘Jack‘; alert(box1.name); //Jack,就近原则, delete box1.name; //删除实例中属性 alert(box1.name); Box.prototype.name = ‘kkk‘//覆盖原型中name属性的值 alert(box1.name);//结果是kkk delete Box.prototype.name;//删除原型中的属性值,之后结果是undefined alert(box1.name);
如何判断属性是在构造函数的实例里,还是在原型里?可以使用 hasOwnProperty()函数来验证:
function Box() {} //声明一个构造函数 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; var box1 = new Box(); alert(box1.name); //Lee,原型里的值 box1.name = ‘Jack‘; alert(box1.name); //Jack,就近原则, alert(box1.hasOwnProperty(‘name‘)); //判断实例是否存在指定属性 实例里有返回 true,否则返回 false
in操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中。
function Box() {} //声明一个构造函数 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; var box1 = new Box(); alert(‘name‘ in box1); //true,存在实例中或原型
我们可以通过hasOwnProperty() 方法检测属性是否存在实例中,也可以通过 in 来判断实例或原型中是否存在属性。那么结合这两种方法,可以判断原型中是否存在属性。
function Box() {} //声明一个构造函数 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; var box1 = new Box(); alert(box1.hasOwnProperty(‘name‘)); alert(‘name‘ in box1); //如果第一个为false说明实例中没有该属性,而第二个为true说明属性存在原型中 //如果第一个为true,说明属性存在实例中
也可以定义一个函数来判段,原理是一样的
function Box() {} //声明一个构造函数 Box.prototype.name = ‘Lee‘; //在原型里添加属性 Box.prototype.age = 100; Box.prototype.run = function () { //在原型里添加方法 return this.name + this.age + ‘运行中...‘; }; function isProperty(object, property) { //判断原型中是否存在属性 return !object.hasOwnProperty(property) && (property in object); } var box1 = new Box(); alert(isProperty(box1, ‘name‘)) //true,如果原型有