您的位置:首页 > 其它

第八章 面向对象之二:function与面向对象的关系(两个实现面向对象的重要属性)

2015-07-20 16:16 429 查看
每一个函数建立完成之后,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__。

isPrototypeOf用法:父类.isPrototypeOf(实例),返回true表明实例的__proto__指向父类的原型对象,false则相反。

Object.getPototypeof(实例):返回实例的__proto__引用,并且Object.getPrototypeOf(实例).原型属性/方法可以直接调用原型的属性和方法。

2、 hasOwnProperty:
用法:实例.hasOwnProperty(属性/方法名)
判断属性或方法名是否来自于实例(或原型),在实例内返回true,在原型中返回false。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: