《JavaScript高级程序设计第六章--面向对象》section_01

JavaScript的对象:是无序属性的集合,其属性值可以使基本值、对象、函数。每个对象都是基于一个引用类型创建的。

6.1.1 属性类型

  参考博文:JavaScript中的属性:定义和赋值的区别

 6.2  创建对象

  之前提到过object构造函数或对象字面量创建单个对象。缺点:使用一个接口创建很多对象,产生大量的重复代码;然后就有了工厂模式。

  6.2.1  工厂模式

    ECMAScript中无法创建类,开发人员就搞了一个函数用来封装特定接口创建对象的细节。形式如下:   

示例代码

    虽然解决了多个相似对象的问题,但却没有解决对象是别的问题,就是怎样知道一个对象是咋样的类型。于是就有了构造函数模式。

  6.2.2  构造函数模式

    构造函数可用来创建特定类型的对象。    

        function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = function(){
                alert(this.name);
            };    
        }
        
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");
        
        person1.sayName();   //"Nicholas"
        person2.sayName();   //"Greg"
        
        alert(person1 instanceof Object);  //true
        alert(person1 instanceof Person);  //true
        alert(person2 instanceof Object);  //true
        alert(person2 instanceof Person);  //true
        
        alert(person1.constructor == Person);  //true
        alert(person2.constructor == Person);  //true
        
        alert(person1.sayName == person2.sayName);  //false        
View Code

     不同之处

  •  没有显式的创建对象;(其实 隐式 var this = new object())
  •    直接将属性和方法赋给了 this对象;
  •    没有return语句;

  构造函数第一个字母一般大写!创建过程有4步:a.new创建一个新对象;b.将构造函数的作用域赋给新对象(因此this就指向了这个新对象);c.执行构造函数中的代码;d.返回新对象;

  这实例有个constructor(构造函数)属性,该属性指向Person;

  构造函数的问题:每个方法都要在每个实例上重新创建一遍,如前例子,person1和person2都是有一个名为sayName()的方法,但那两个方法不是同一个Function的实例。不要忘了--ECMAScript中的函数是对     象,因此每定义一个函数,也就实例化了一个对象(this.sayName = new Function("alert(this.name)");//与声明函数在逻辑上是等价的。)会导致不同的作用域链和标识符解析,但是创建Function新实例的机制仍然  是相同的。因此,不同实例上的同名函数是不相等的。例如 alert(person1.sayName == person2.sayName);//false

  构造函数问题解决探讨

        function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = sayName;
        }
        
        function sayName(){
            alert(this.name);
        }
        
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");
        
        person1.sayName();   //"Nicholas"
        person2.sayName();   //"Greg"
        
        alert(person1 instanceof Object);  //true
        alert(person1 instanceof Person);  //true
        alert(person2 instanceof Object);  //true
        alert(person2 instanceof Person);  //true
        
        alert(person1.constructor == Person);  //true
        alert(person2.constructor == Person);  //true
        
        alert(person1.sayName == person2.sayName);  //true
示例代码

    这里吧sayName()函数的定义转移到构造函数外部。而在构造函数内部,将sayName属性设置成等于全局的sayName函数。因为这是指针,实例对象就共享全局作用域中定义的同一个syaName()函数。但问题又来了一个!你放在全局作用域,只为某个对象调用,让全局作用域的面子往哪搁,如果需要很多对象,意味着在全局作用域中要定义很多全局函数,丝毫没有封装可言了!——————>嗯,原型模式来解决。

6.2.3  原型模式 

  我们创建的每个函数都有一个prototype(原型)属性(联想到每个实例对象有个constructor属性指向构造函数),是个指针,指向一个对象(包含可以由特定类型的所有实例共享的属性和方法),这对象就是通过构造函数创建实例对象的原型对象。使用原型对象好处是可以让所有实例对象共享它所包含的属性和方法诶。根部不需再构造函数中定义实例对象的信息,而是将这些信息直接添加到原型对象中。

 1         function Person(){
 2         }
 3         
 4         Person.prototype.name = "Nicholas";
 5         Person.prototype.age = 29;
 6         Person.prototype.job = "Software Engineer";
 7         Person.prototype.sayName = function(){
 8             alert(this.name);
 9         };
10         var person1 = new Person();
11         person1.sayName(); //"Nicholas"
12        
13         var person2 = new Person();
14         person2.sayName();//"Nicholas"
15         alert(person1.sayName == person2.sayName);//true

  这就把这些个函数封装在构造函数创建实例对象的原型对象中去了,且调用的函数都是来自原型对象。属性也可以。

  理解原型对象

   无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性;他们的关系

   Person.prototype.constructor指向Person。

   创建自定义的构造函数后,其原型对象默认只会取得constructor属性;至于其他方法,则都是从Object继承。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问到它,但可以通过Object.getPrototypeOf()方法间接的读取到它的值。(注:Firefox、Safari和Chrome在每个对象上都支持一个__proto__;)。正真要明白的是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。

 

  alert(Person.prototype.isPrototypeof(person1));//true.且ECMAScript5增加了一个新方法,叫Object.getPrototypeOf()返回的对象实际就是这个对象的原型。我们利用原型实现继承稍后讨论。

  每当代码读取某个对象属性时,会执行搜索,先是搜索对象本身是否有这个属性,有就返回该属性的值,没有就继续搜索指针指向的原型对象,看看是否有给定名字的属性,有就返回。这正是多个对象实例共享原型所保存的属性和方法的基本原理。

注:虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值

 1         function Person(){
 2         }
 3         
 4         Person.prototype.name = "Nicholas";
 5         Person.prototype.age = 29;
 6         Person.prototype.job = "Software Engineer";
 7         Person.prototype.sayName = function(){
 8             alert(this.name);
 9         };
10         
11         var person1 = new Person();
12         var person2 = new Person();
13         
14         person1.name = "Greg";
15         alert(person1.name);   //"Greg" ?from instance
16         alert(person2.name);   //"Nicholas" ?from prototype
17         alert(Person.prototype.isPrototypeOf(person1));
18         alert(Object.getPrototypeOf(person1)==Person.prototype);
19         alert(person1.constructor);//指向构造函数

    如果删除了上面例子中person1.name = "Greg";就恢复了对原型中name属性的连接。

  介绍一个hasOwnProperty()方法,可以检测一个属性是存在于实例中,还是存在于原型中。这个方法继承自Object,只在给定属性存在于对象实例中,才会返回true.;例如上例中person1.name = "Greg"alert(person1.hasOwnProperty("name"));//true。如果删除delete person1.name 就返回false;

  (注:ES5的Object.getOwnPropertyDescriptor()方法只能用于实例属性,要取得原型属性的描述符,必须直接在原型对象上调用Object.getOwnPropertyDescriptor())

  

   

 

posted @ 2016-02-25 14:52  短短几年当校长  Views(150)  Comments(0)    收藏  举报