标签:
每一个函数建立完成之后,javaScript都会自动的给它添加两个属性和一个配套对象(配套类),即prototype属性和__proto__属性,而配套对象(类)叫做“原型对象”。
只要是函数,它就有这个原型对象,我们无法删除它。
原型对象的意义在于,它包含的属性和方法对于通过此函数创建的实例来说,是公用的。而我们在函数本身内部定义的属性和方法不是公用的,每次建立实例之后,虽然两个实例都具有想通的属性和方法,但是实际上是两份相同的拷贝,并不是同一段内存的属性和方法,它们的各自修改不会对其他实例造成影响,但是这对于内存的高效率使用是很不方便的。
函数(类)才有原型对象属性(prototype属性),实例是没有的。这对于javaScript自带的构造函数(Array、Boolean、RegExp等)也是一样的。
我们如何使用原型对象?函数本身的prototype属性保存的就是原型对象的地址,我们可以通过prototype属性访问(设置)原型对象的属性和方法(函数.prototype.属性/方法)。
原型对象本身跟普通对象(函数、类)是基本一样的,它也可以有自定义的属性和方法,区别在于以下两点:
一、 它没有prototype属性。(其实可以理解,本身已经是原型对象了)
二、 它有一个constructor属性,保存的是函数(类)本身的地址。也就是通过constructor属性,原型对象可以找到它所配套的原始函数。(也就是它知道它是谁的原型对象)。
<script>
function Person() { // 创建一个空函数(类)
}
// 添加原型属性和方法
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "sofeware Engineer";
Person.prototype.sayName = function () {
console.log( this.name );
};
// Person.prototype = {
// person1和person2的sayName是同一个内存地址的函数
var person1 = new Person();
person1.sayName();
var person2 = new Person();
person2.sayName();
console.log( person1.sayName === person2.sayName );
// -----------------------------------------------------
// 看看函数是否有perototype属性(有的,而且Array是js自带的构造函数,当然也有)
console.dir( Array );
// -----------------------------------------------------
// 看实例是否有prototype属性(实际没有,但是有__proto__属性)
var a = new Array();
console.dir( a );
console.dir( person1 );
// -----------------------------------------------------
</script>
请仔细查看console输出的信息,对照我们上边整理的知识点。
__proto___跟prototype一样,都是函数自带的属性,当然这个属性不止是函数有,通过函数(类)创建的实例也是有的。
在ECMAScript里管这个属性称为“[[Prototype]]”,在浏览器里的实现称为:__proto__。它包含的是指向创造这个实例函数(类)的原型对象。听着有点晕,就是实例一般都是通过new 函数()这种表达式创造出来的,这个实例的__proto__属性指向的就是这个函数的原型对象。
__proto__本身是无法修改的,但是某些浏览器为了方便把__proto__暴露出来了,我们可以对他进行修改,比如firefox和chrome。
__proto__属性是javaScript实现原型链的关键,javaScript实现面向对象的继承是基于原型链的,因此__proto__属性是javaScript实现继承的根本,搞清楚__proto__的真相就理解了继承。
我们说所有的函数和实例都包含__proto__属性,javaScript内部的称为内建类型的Array类型(实际上Array是一个函数)也有,它的__proto__的指向应该是它的父类的原型对象,也就是Function的原型对象。
Object在javaScript里是所有函数(类)的基类,实际上javaScript自带的内建类型(Array,Boolean,String,Function,RegExp,Error等)的父类都是object。
在javaScript里,可以理解为所有的函数都是通过Function关键字来进行初始化的,比如Person(){}这个空函数,实际上可以理解为Person = new Function(),也就是说,我们创建一个自定义函数Person,它的__proto__应该指向Function的原型对象,而Function原型对象的__proto__指向Object的原型对象,Object的__proto__是null。如此,构成了从实例到根父类的连接,这就是原型链。
因此可以说,函数是一个特殊的存在,它既是一个类,也是一个实例。
当执行实例的一个方法时,javaScript这么规定,先找实例本身是否有此方法,没有按照 __proto__的指向找向父类的对象,如果父类对象没有这个方法则找父类对象的原型对象,看父类的原型对象是否有此方法,如果没有按照父类原型对象的__proto__的指向找父父类的原型对象,父父类对象没有去找父父类对象的原型对象,如果找到那么执行,如果还找不到就继续向上,一直到object的原型对象的__proto__指向的null。
我们可以通过实例的__proto__访问父类原型对象的属性和方法,但是无法更改它,我们自定义一个同名的属性或方法会覆盖掉父类的属性和方法。如果我们覆盖了同名属性或方法,通过delete操作符可以删除实例的同名属性或方法,从而重新访问父类的属性和方法。
person1.name = "Greg";
person1.sayName(); // 来自实例
person2.sayName(); // 来自继承
delete person1.name;
person1.sayName(); // 来自继承
person2.sayName(); // 来自继承
有关原型的几个函数:
1、 isPrototypeOf/Object.getPrototypeof():访问__proto__。
2、 hasOwnProperty:
用法:实例.hasOwnProperty(属性/方法名)
判断属性或方法名是否来自于实例(或原型),在实例内返回true,在原型中返回false。
第八章 面向对象之二:function与面向对象的关系(两个实现面向对象的重要属性)
标签:
原文地址:http://www.cnblogs.com/jingubang/p/4661751.html