如何使用JavaScript创建对象

来源:互联网 发布:淘宝上怎样买东西 编辑:程序博客网 时间:2024/04/29 07:25

如何使用JavaScript创建对象

标签(空格分隔): 理解对象属性 理解并创建对象


面向对象的语言都有标志:类的概念,通过类,我们创建很多个拥有相同属性和方法的对象。但是我们都知道,JavaScript中没有类的概念,因此其创建对象的方式也有其他语言(O O)有所不同;
我们都知道,JavaScript可以通过Object构造函数或者对象字面量来创建对象,但是这种方式有一个很明显的缺点,那就是会产生大量的重复代码,这里,主要介绍几种JavaScript中常用的创建对象的方法;

1.工厂模式

因为在JavaScript中无法创建类,因此开发人员就发明了一种函数,用函数来封装,以特定的接口来创建对象的细节:function createPeople(name,age,job){    var people=new Object();    people.name=name;    people.age=age;    people.job=job;    people.sayHi=function()    {        alert("Hi,my name is "+this.name);    }    return people;}var jack=createPeople("jack",21,"student");jack.sayHi()//Hi,my name is jackvar tom=createPeople("tom",22,"student");缺点:解决了创建相似对象的问题,却无法解决对象识别的问题;

2.构造函数模式

function People(name,age,job){    this.name=name;    this.age=age;    this.job=job;    this.sayHi=function()    {        alert("Hi,my name is "+this.name);    }}var jack=new People("jack",21,"student");var tom =new People("tom",22,"student");alert(jack instanceof People);//truealert(jack instanceof Object);//truealert(jack.sayHi==tom.sayHi)//false优点:可以将自定义的构造函数的实例作为一种特定的类型,可以解决对象识别的问题,而这正是构造函数模式胜过工厂模式的地方;    构造函数与函数的唯一区别就在于,调用的方式{    使用new操作符来调用:构造函数;    不使用:普通函数    }缺点:使用构造函数的主要问题就在于:每个方法都要在每个实例上重新创建一遍,因此不同实例上的同名函数是不相等的。我们可以使用下面的方法来解决这个问题(虽然会引入新的问题):function People(name,age,job){    this.name=name;    this.age=age;    this.job=job;    this.sayHi=sayHi;}function sayHi(){    alert("Hi,my name is "+this.name);}var jack=new People("jack",21,"student");var tom =new People("tom",22,"student");优点:这样做解决了上面的问题;缺点:但是是弊大于利的,因为sayHi包含的是一个指向某个函数的指针,因此jack和tom就共享了一个在全局作用域中定义的sayHi()函数。在全局作用域的函数只能被某个函数调用,这让全局作用域有点名不符实;最坑爹的是,如果这个对象需要定义很多个方法,那就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。

3.原型模式

原型模式的出现就是为了解决上面那个问题的:function People(){};People.prototype.name="jack";People.prototype.age=21;People.prototype.job="student";People.prototype.sayHi=function(){   alert("Hi,my name is "+this.name);  }var jack=new People();jack.sayHi();//Hi,my name is jackvar tom=new People();tom.name="tom";tom.sayHi();//Hi,my name is jack

理解原型对象

无论什么时候,只要创建了一个新函数,就会为这个函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。
即,我们创建了一个People()函数,就会默认为这个People函数创建一个属性,这个属性指向People函数的原型对象Object,Object对象会获得一个constructor(构造函数)属性,这个属性指向People函数。可以理解为我们通过Object的构造函数创建了一个People函数。
当我们创建了自定义的构造函数之后,其原型对象默认只会获得constructor属性,至于其他方法,都是从Object中继承而来的。当调用构造函数创建一个新实例之后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。这个指针ES5把它叫做prototype。这是一个内部属性,在脚本中没有标准的方式去访问它,但是,FireFox,Safari和Chrome在每个对象上都支持一个属性_ proto ,所有对象的 proto 都指向其构造函数的prototype。(这个 proto _可以理解为是内部属性prototype)有一点需要明确的是,这个连接是存在于实例与构造函数的原型对象之间的,而不是实例与构造函数之间的。
在这里,jack的 _ proto _指向其构造函数的prototype(也就是People的prototype ==>Object)
jack. __ proto ==Object;(一般只有构造函数创建的实例对象才会去考虑 __proto 属性)
People.prototype==Object;
jack. _ proto _==People.prototype;
Object.constructor==People(){};
(1)虽然在所有的实现中,都无法访问到[[prototype]],但是可以通过isPrototypeOf()来确定对象之间是否有这种关系。如果[[prototype]]指向调用isPrototypeOf()方法的对象,那么这个函数就返回true;
alert(People.prototype.isPrototypeOf(jack));//true.
(2) ES5新增了一个方法,叫做Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[prototype]]的值;
alert(Object.getPrototypeOf(jack)==People.prototype);//true
alert(Object.getPrototypeOf(jack).name);//jack
(3)虽然可以通过对象实例访问保存在原型中的值,但是却不能通过对象实例重写原型中的值。如果我们为对象实例添加一个属性,那么就会屏蔽原型对象中保存的同名属性。
(4)使用hasOwnProperty()可以检测一个属性是存在于对象的实例中还是其原型对象中。

原型与in操作符

有两种方式使用in操作符,单独使用和在for-in中使用。
单独使用时:in操作符会在通过对象能够访问给定属性时返回true,而不管它是存在于对象实例中还是原型中。
eg:function hasPrototypeProperty(object,name)
{
return !object.hasOwnProperty(name)&&(name) in Object;
}
上面方法可以检测某个属性是否在原型中。

更简单的原型语法

其实,我们还可以将上面的代码优化一下,使用对象字面量来重写整个原型对象。
function People(){};
People.prototype={
name:”jack”,
age:21,
job:”student”,
sayHi:function()
{
alert(“Hi,my name is “+this.name);
}
}
注意,这样写的话,相当于重写了默认的prototype对象,因此其constructor属性也就不再指向People()了。当然我们也可以将其手动的更改回来。
function People(){};
People.prototype={
constructor:People,
name:”jack”,
age:21,
job:”student”,
sayHi:function()
{
alert(“Hi,my name is “+this.name);
}
}

原型的动态性

我们可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不一样了。
我们知道调用构造函数会为实例添加一个指向一个指向最初原型的[[prototype]]( _ proto_ )属性,而重写原型就相当于切断了构造函数与最初原型之间的关系。

原生对象的原型

原型模式的重要性不仅体现在创建自定义类型方面,就连所有的原生引用类型,也都是通过这种模式创建的。所有原生引用类型都在其构造函数的原型上定义了方法,例如Array.prototype中就可以找到sort()方法。
通过原生对象的原型,不仅可以取得所有默认方法的引用,还可以定义新方法。可以像修改自定义对象的原型一样修改原生对象的原型。

原型对象的缺点

1.原型对象构建的所有实例在默认情况下都会取得相同的属性值;(因为其省略了为构造函数传递初始化参数的环节);2.如果包含引用类型值的属性的话,由于该属性是存在于原型对象中的,所以在一个实例化对象上的修改会影响到另一个实例化对象的属性值;function People(){};People.prototype={    constructor:People,    name:"alice",    age:22,    job:"student",    friends:["jack","tom"],    sayHi:function()    {        alert("Hi,my name is "+this.name);     }}var objA=new People();var objB=new People();objA.friends.push("michael");alert(objA.friends);//jack,tom,michaelalert(objB.friends);//jack,tom,michaelalert(objA.friends==objB.friends);//true

4.组合使用构造函数模式和原型模式

这种是目前创建自定义类型的最常见方式,使用构造函数用于定义实例属性,原型模式用于定义方法和共享的属性。结果就是每个实例都会有一份的实例属性的副本,但同时又共享着对方法的引用,最大限度的节省了内存。于此同时,这种混合模式还能够向构造函数传参进行初始化实例。function People(name,age,job){    this.name=name;    this.age=age;    this.job=job;}People.prototype={    constructor:People,    sayHi:function()    {        alert("Hi,my name is "+this.name);     }}var jack=new People("jack",21,"student");var tom=new People("tom",22,"student");

5.动态原型模式

动态原型模式是一种致力于在构造函数中初始化原型的模式(仅在必要的情况下),同时又能保持组合使用构造函数与原型的优点,是一种较为完美的解决方案。function People(name,age,job){    this.name=name;    this.age=age;    this.job=job;    if(typeof this.sayHi!="function")    {        People.prototype.sayHi=function()        {            alert("Hi,my name is "+this.name);         }    }}var jack=new People("jack",21,"stduent");jack.sayHi();优点:(1).每一次通过构造函数实例化对象的时候,相应的原型对象只需要初始化一次就好了;     (2).只需要检查原型中一个属性是否存在即可判断是否需要初始化原型对象了。注意:使用动态原型模式时,不能使用对象字面量重写原型。

6.寄生构造函数模式

function People(name,age,job){    var person=new Object();    person.name=name;    person.age=age;    person.job=job;    person.sayHi=function()    {        alert("Hi,my name is "+this.name);     }    return person;}var jack=new People("jack",21,"student");jack.sayHi();说明:返回的对象与构造函数或者构造函数的原型属性之间是没有半毛钱关系的。建议:如果你能够使用其他模式,别用它。

稳妥构造函数模式

所谓稳妥对象,就是指没有公共属性,而且其方法也不引用this的值。稳妥对象最适合在一些安全的环境中,或者在防止数据被其他应用程序改动时使用。function (name,age,job){    var o=new Object();    o.name=name;    o.age=age;    o.job=job;    o.sayHi=function()    {        alert(name+",Hi");    };    return o;}var jack=new People("jack",21,"student");jack.sayHi();
0 0
原创粉丝点击