创建对象《javascript高级程序设计》阅读笔记

下面记录一下创建对象的几种方式:

1,最简单的方法:

  var person=new Object();

  person.name="rason2008";

  person.age=20;

  person.job="student";

  person.sayName=function(){

    alert(this.name);

  };

  此方法缺点:创建很多对象会产生大量重复代码,下面的工厂模式可以解决此问题

2,工厂模式:

  function createPerson(name ,age,job){

    var o=new Object();

    o.name=name ;

    o.age=age;

    o.job=job;

    o.sayName=function(){

    alert(this.name);

    };

    return o;

  }

  此方法缺点:没有解决对象识别的问题(即怎么知道一个对象的类型),下面的构造函数模式可以解决这个问题

3,构造函数模式:

  function Person(name ,age,job){

    this.name=name ;

    this.age=age;

    this.job=job;

    this.sayName=function(){

      alert(this.name);

    };

  }

  以这种方式调用构造函数实际上会经历一下四个步骤:

  (1)创建一个新的对象;

  (2)将构造函数的作用域赋给新对象(因此this就指向了这个新对象)

  (3)执行构造函数中的代码

  (4)返回新对象

  构造函数解决对象识别问题:

  var person1=new Person("rason2008",20,"student");

  var person2=new Person("girlFriend",20,"student");

  alert(person1.constructor==Person);  //true

    alert(person2.constructor==Person);  //true

  不过检测对象类型,一般还是用instanceof操作符更加可靠

  alert(person1 instanceof Object);   //true

  alert(person1 instanceof Person);   //true

  alert(person2 instanceof Object);   //true

  alert(person2 instanceof Person);   //true

  将构造函数当作函数:

  构造函数与其它函数的唯一区别,就在于它们的调用方式不同。任何函数,只要通过new操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new来调用,那它跟普通函数也不会有神马两样。

  var person1=new Person("rason2008",20,"student");   //当作构造函数来调用

  person1.sayName();   //rason2008 

  Person("rason2008",20,"student");    //作为普通函数调用

  window.sayName();   //rason2008

  var o= new Object();

  Person.call(o,"rason2008",20,"student");     //在另一个对象的作用域调用

  o.sayName();    //rason2008

  构造函数存在的问题:

  (1) function Person(name ,age,job){

    this.name=name ;

    this.age=age;

    this.job=job;

    this.sayName=function(){

      alert(this.name);

    };

    }

   继续拿这个例子来说,如果我们创建了两个Person对象,person1和person2,那么也创建了两个完成同样任务的Function实例;况且有this对象在,根本不用在执行代码前就把函数绑定到特定对象上面。

  可以用下面的方式解决这个问题:

  function Person(name ,age,job){

    this.name=name ;

    this.age=age;

    this.job=job;

    this.sayName=sayName;

    }

  function sayName(){

      alert(this.name);

    };

  但这也导致了新的问题:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就没有丝毫封装性可言了。

  解决上面遇到的问题就要用到下面讨论的原型模式了:

4,在介绍最佳创建对象的方法之前要先介绍一下原型模式:

  我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。那么显而易见,使用原型的好处是可以让所有对象实例共享它所包含的属性和方法。

  function Person(){

  }

  Person.prototype.name="rason2008";

  Person.prototype.age=20;

  Person.prototype.job="student";

  Person.prototype.sayName=function(){

  alert(this.name);

  };

  var person1=new Person();

  var person2=new Person();

  person1.sayName();   //"rason2008"

  person2.sayName();   //"rason2008"

  alert(person1.sayName==person2.sayName);   //true

  理解原型:

  (1)只要创建一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,该属性指向该函数的原型

  (2)函数的原型会自动获得一个constructor属性,该属性指向prototype属性所在的函数

  (3)当调用构造函数创建一个新实例后,该实例会获得一个指向构造函数的原型的属性_proto_

  上面的例子如下图:

  alert(Person.prototype.isPrototypeOf(person1));  //true

  alert(Person.prototype.isPrototypeOf(person2));  //true

  每当代码读取某个对象的某个属性时,都会执行一次搜索:先在实例中找给定的属性名,找不到则继续搜索指针指向的原型对象,就是沿着原型链找,一找到就返回。所以如果实例中定义了和原型中同名的属性,那么原型中的该属性就会被屏蔽掉,但它还是存在原型中的,只是利用实例访问不了而已,要访问原型中被屏蔽的属性则要先使用delete操作符删除实例属性。

  使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中,存在实例中时会返回true。

  而单独使用in操作符时,无论属性存在实例中还是原型中都会返回true。

  因此,由上面两个知识点可以写一个方法判断属性只存在于原型中:

  function hasPrototypeProperty(object,name){

    return !object.hasOwnProperty(name)&&(name in object);

  }

  更简单的原型语法:

  大家应该注意到了,前面例子每添加一个属性和方法就要敲一边Person.prototype。下面介绍一个简单的方法:

  function Person(){}

  Person.prototype={

    name:"rason2008",

    age:20,

    job:"student",

    sayName:function(){

      alert(this.name);

    }

   }

  但是要注意的是此时constructor属性不再指向Person。前面说过,每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。此种方法本质上重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数)。

  因此我们可以像那面那样特意将constructor设置回适当的值:

  function Person(){}

  Person.prototype={

    constructor:Person

    name:"rason2008",

    age:20,

    job:"student",

    sayName:function(){

      alert(this.name);

    }

   }

  原型的动态性:

  下面让大家观察两个例子来理解原型的动态性:

  function Person(){}

  var person=new Person();

  Person.prototype={

    constructor:Person

    name:"rason2008",

    age:20,

    job:"student",

    sayName:function(){

      alert(this.name);

    }

   }

  person.sayName();    //error
  为什么会发生错误?因为原型被重写了,person还是指向原来的默认原型,默认原型并没有定义sayName()方法,所以会发生错误。把它改成下面的样子就不会发生错误了:

  function Person(){}

  Person.prototype={

    constructor:Person

    name:"rason2008",

    age:20,

    job:"student",

    sayName:function(){

      alert(this.name);

    }

   }

  var person=new Person();

  person.sayName();   //"rason2008"
  原型对象存在的问题:
  依然是看例子:
  function Person(){}

  Person.prototype={

    constructor:Person

    name:"rason2008",

    age:20,

    job:"student",

    friends:["girlfriends1","girlfriends2","girlfriends3"] ,  //谁不想那么多girlfriends,搞IT伤的起吗

    sayName:function(){

      alert(this.name);

    }

   }

  var person1=new Person();

  var person2=new Person();

  person1.friends.push("girlfriend4");

  alert(person1.friends);   //"girlfriends1","girlfriends2","girlfriends3","girlfriend4"

  alert(person2.friends); //"girlfriends1","girlfriends2","girlfriends3","girlfriend4"  看出问题所在了吗?你弟弟增加了一个女盆友你也增加了同一个女盆友,问题很严重

  alert(person1.friends===person2.friends);  //true   你的女盆友就是你弟弟的女朋友,are you crazy?

5,终于轮到了介绍最最常用的创建自定义对象的方法了:

  此方法综合构造函数和原型模式的优势,perfect了木有?

  function Person(name,age,job){

    this.name=name;

    this.age=age;

    this.job=job;

    this.friends=["girlfriends1","girlfriends2","girlfriends3"]  ;

  }

  Person.prototype={
    constructor:Person,
    sayName:function(){
      alert(this.name);
    }
  }
  var person1=new Person("rason2008",20,"student");
  var person2=new Person("ra",20,"IT");
  person1.friends.push("girlfriend4");

  alert(person1.friends);   //"girlfriends1","girlfriends2","girlfriends3","girlfriend4"

  alert(person2.friends); //"girlfriends1","girlfriends2","girlfriends3"

  alert(person1.friends===person2.friends);       //false
  alert(person1.sayName===person2.sayName);  //true
  终于写完了创建对象的笔记,怎一个累字了得,写一篇笔记起码得花两个钟啊!
posted @ 2011-10-08 17:11  rason2008  阅读(280)  评论(0编辑  收藏  举报