日期:2014-05-16  浏览次数:20361 次

JavaScript面向对象的程序设计2—理解原型

1. 原型模式
??? JavaScript中我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。
??? 如果按照字面意思来理解,那么prototye就是通过调用构造函数而创建的那个对象的原型对象。
??? 使用原型对象的好处就是可以让所有对象实例共享它所包含的属性和方法。
??? 或句话说,就是不必在构造函数中定义对象信息,而是可以将这些信息直接添加到原型对象中。

function Person() {}
//将属性和方法添加到Person的prototype属性中,构造函数变成了空函数
Person.prototype.name = "answer";
Person.prototype.age = 22;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() {
    alert(this.name);
}
var person1 = new Person();
person1.sayName();    //answer
var person2 = new Person();
person2.sayName();     //answer
//不同的实例同享对象原型中的方法
alert(person1. sayName == person2.sayName);    //true

?

2. 理解原型
??? 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数生成一个prototype属性。
??? 在默认情况下,所有prototype属性都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。
??? 如上面Person.prototype.constructor指向Person。而通过这个构造函数,我们还可以继续为原型添加其他属性和方法。

??? 创建了自定义的构造函数后,其原型属性默认只会取得contructor属性;至于其他方法,则都是从Object继承而来。
??? 当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(一般为"_proto_",内部属性),指向构造函数的原型属性。(一些浏览器中,这个属性对脚本可见)
??? 要明确的重要一点就是:就是这个连接存在于实例与构造函数的原型属性之间,而不是存在于实例与构造函数之间。
??? 虽然实例的内部_proto_指针在一些实现中无法访问,但是,所有实现中都可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。
??? 如果对象的_proto_指向调用isPrototypeOf()方法的对象,那么这个方法就返回true,如下所示:

alert(Person.prototype.isProtorypeOf(person1));    //true
alert(Person.prototype.isProtorypeOf(person2));    //true

?

3. 查找对象属性的过程
??? 每当代码读取某个对象的某个属性的时候,都会执行一次搜索,目标是具有给定名字的属性。
??? 搜索首先从对象实例本身开始。
??? 如果在实例中找到了具有给定名字的属性,则返回该属性的值;
??? 如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找到具有给定名字的属性。
??? 如果在原型对象中查找了这个属性,则返回该属性的值。
??? 比如调用person1.sayName()方法,先在实例person1中查找sayName方法,
??? 如果没有,则继续查找person1的原型有sayName属性吗,如果有,则返回,调用sayName方法。

?

??? 可以通过对象实例访问保存在原型中的值,却不能通过对象实例重写原型中的值。
??? 如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,
??? 那么我们就在实例中创建了该属性,该属性将会屏蔽原型中的那个属性(注意是在访问时屏蔽,而不是重写)。

     //以“1”中的Person对象为例
   person1.name = "xhc";    //为person1添加name属性
   alert(person1.name);       //xhc 来自person1实例
   alert(person2.name);       //answer 来自person2的原型

??? 当为一个对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名的属性;如上所示。
??? 添加的属性会阻止我们访问原型中的值,但不会修改原型中的那个值;如上面所示访问person2.name.
??? 但是可以通过delete操作符完全删除实例属性,从而让我们能够重新访问原型中的属性。

      //以”1“中的Person对象为例
    person1.name = "xhc";    //为person1添加name属性
    alert(person1.name);       //xhc 来自person1实例
    alert(person2.name);       //answer 来自原型
    delete person1.name;      //删除person1的实例属性
    alert(person1.name);       //answer 来自原型

?

?4. 实例属性检查
??? 使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中,只有属性存在于对象实例中时,才返回true。(该方法从Object继承而来)
???

    //以“1”中"的Person对象为例
   alert(person1.hasOwnProperty("name"));     //false
    person1.name = "xhc";                                   //为实例添加属性
   alert(person1.hasOwnProperty("name"));      //true
    delete person1.name;                                     //删除实例属性
   alert(person1.hasOwnProperty("name"));      //false

?

5. 原型与in操作符
??? 有两种方式使用in操作符:单独使用和在for-in循环中使用。
??? 1) 单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例还是原型中。
????????因此同时使用in操作符和hasOwnProperty()方法,就可以确定一个属性到底是存在于对象实例中,还是对象原型中。

        //判断一个属性是存在于原型中
     function hasPrototypeProperty(obj, name) {
            return !(obj.hasOwnProperty(name")) && (name in obj);
        }

?

?2) 使用for-in循环时,返回的是所有能够通过对象访问的,可枚举的(enumerated)属性,其中即包括存在于实例中的属性,也包括存在于原型中的属性。
?????屏蔽了原型中不可枚举属性(即设