首页 > 网站 > WEB开发 > 正文

JavaScript 对象的创建

2024-04-27 14:17:45
字体:
来源:转载
供稿:网友

javaScript 对象的创建

Object类型是Javascript中使用最多的一种类型。创建Object实例的方式有多种,接下来一一列举。

1. Object构造函数

varperson=newObject();person.name="Brittany";person.age=23;person.job="webfront-endengineer";person.sayName=function(){console.log(this.name);};person.sayName();//Brittany

2.对象字面量模式

varperson={name:"Brittany",age:23,job:"webfront-endengineer",sayName:function(){console.log(this.name);}};person.sayName();

虽然Object构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,可以使用工厂模式的一种变体。

3. 工厂模式

functioncreatePerson(name,age,job){varo=newObject();o.name=name;o.age=age;o.job=job;o.sayName=function(){console.log(this.name);};returno;}varperson1=createPerson("Brittany",23,"SoftwareEngineer");varperson2=createPerson("Sam",26,"SoftwareEngineer");console.log(typeofperson1);//Object

工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。如代码中只能检测出person1为Object类型。随着JavaScript的发展,又一个新模式出现了。

4. 构造函数模式

functionPerson(name,age,job){this.name=name;this.age=age;this.job=job;this.sayName=function(){console.log(this.name);}}varperson1=newPerson("Brittany",23,"Webfront-endengineer");varperson2=newPerson("Closure",26,"Manager");person1.sayName();person2.sayName();console.log(person1.sayName==person2.sayName);//false

使用构造函数的主要问题:每个方法都要在每个实例上重新创建一遍。如代码中所示,person1的sayName和person2的sayName不相等。可以将函数定义转移到构造函数外部来解决。

functionPerson(name,age,job){this.name=name;this.age=age;this.job=job;this.sayName=sayName;}functionsayName(){console.log(this.name);}

sayName函数的定义转移到了构造函数外部。而在构造函数内部,我们将sayName属性设置成等于全局的sayName函数。这样一来,由于sayName包含的是一个指向函数的指针,因此person1和person2对象就共享了在全局作用域中定义的同一个sayName()函数。这的确解决了两个函数做同一件事的问题,可是新问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让让人无法接受的是:如果需要定义很多方法,那就要定义很多个全局函数,于是这个自定义的引用类型就无封装性可言。这些问题可通过使用原型模式来解决。

5. 原型模式

1)对象创建

functionPerson(){}Person.PRototype.name="Brittany";Person.prototype.age=23;Person.prototype.job="Webfront-endengineer";Person.prototype.getName=function(){console.log(this.name);};varp1=newPerson();varp2=newPerson();console.log(p1.name);//Brittanyconsole.log(p2.name);//Brittanyconsole.log(p1.getName==p2.getName);//true

实例中创建的属性会覆盖原型中的同名属性,不能修改原型中的属性。

p1.name="Susan";console.log(p1.name);//Susan

hasOwnProperty()检测一个属性是否存在于实例中。

console.log(p2.hasOwnProperty("name"));//falsep2.name="koko";console.log(p2.hasOwnProperty("name"));//truedeletep2.name;console.log(p2.hasOwnProperty("name"));//false

isPropertyOf()

console.log(Person.prototype.isPrototypeOf(p1));//trueconsole.log(Person.prototype.isPrototypeOf(p2));//true

getPropertyOf()

console.log(Object.getPrototypeOf(p1)==Person.prototype);//trueconsole.log(Object.getPrototypeOf(p1));//Person

2)原型与in操作符

in单独使用时,通过对象访问到特定属性时返回true,无论该属性存在于实例中还是原型中。hasOwnProperty(),通过对象访问到特定属性时返回true,且该属性存在于实例中。

varp3=newPerson();console.log("name"inp3);//trueconsole.log(p3.hasOwnProperty("name"));//falsep3.name="insist";console.log(p3.hasOwnProperty("name"));//true

确定属性到底是存在于对象中,还是存在于原型中。如下函数hasprototypePropery()返回true表示该属性存在于原型中,而不是存在于实例中。

functionhasPrototypeProperty(object,name){return!hasOwnProperty("name")&&(nameinobject);}

for..in循环,所有通过对象能够访问的,可枚举的(enumerated)属性,既包括存在于实例中的属性,也包括存在于原型中的属性。

for(varpropinp1){console.log(prop);//nameagejobsayName}

Object.keys(),ECMAScript5的方法,取得对象上所有可枚举的属性,接收一个对象作为参数,返回值是一个包含所有可枚举属性的字符串数组。注意:Person.prototype也是对象。

varkeys=Object.keys(Person.prototype);console.log(keys);//["nameagejobsayName"]varp1=newPerson();console.log(Object.keys(p1));//[]p1.name="get";console.log(Object.keys(p1));//["name"]

Object.getOwnPropertyNames(),得到所有实例属性,无论它是否可枚举。

varkeys=Object.getOwnPropertyNames(Person.prototype);console.log(keys);//["constructor","name","age","job","getName"]varkeys_p1=Object.getOwnPropertyNames(p1);console.log(keys_p1);//[]

3)更简洁的原型语法

functionPerson(){}Person.prototype={name:"Brittany",age:23,job:"Webfront-endengineer",getName:function(){returnthis.name;}};varfriend=newPerson();console.log(friendinstanceofPerson);//trueconsole.log(friendinstanceofObject);//trueconsole.log(friend.constructor==Person);//falseconsole.log(friend.constructor==Object);//false

在上面的代码中,将Person.prototype设置为等于一个对象字面量形式创建的新对象,最终结果相同。但有一个例外:constructor属性不再指向Person了。每创建一个函数,就会同时创建它的prototype对象,这个对象也会自动获得constructor属性。而我们在这里使用的语法,本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),不再指向Person函数。此时尽管instanceof操作符还能返回正确的结果,但通过constructor已经无法确定对象的类型了。

通过如下方式,将constructor手动设置为合适的值。

Person.prototype={constructor:Person,name:"Brittany",age:23,job:"Webfront-endengineer",getName:function(){console.log(this.name);}};

4)原型的动态性

在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来——即使是先创建了实例后修改原型也照样如此。

varfriend=newPerson();Person.prototype.sayHi=function(){console.log("hi");};friend.sayHi();

尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,情况就不一样了。

functionPerson(){}varfriend=newPerson();Person.prototype={constructor:Person,name:"Brittany",age:23,job:"Webfront-endengineer",getName:function(){console.log(this.name);}};friend.getName();//error

若是创建实例放在重写原型对象之后,则不会报错。

5)原生对象的原型

所有原生引用类型(Object、Array、String)都在其构造函数的原型上定义了方法,如:Array.prototype.sort()、String.prototype.subString(), 通过原生对象的原型可以取得所有默认方法的引用,并可以定义新方法。

console.log(typeofArray.prototype.sort);//functionconsole.log(typeofString.prototype.substring);//functionString.prototype.startsWith=function(text){returnthis.indexOf(text)==0;};varmsg="HelloWorld";console.log(msg.startsWith("Hello"));//true

6)原型对象的问题

缺点一:省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下将取得相同的属性值。

缺点二:原型中所有属性被许多实例共享,这种共享对于函数非常适合。对于包含基本值属性倒也说得过去,因为通过在实例上添加一个同名属性,可以隐藏原型中对应的属性。但对于包含引用类型值得属性来说,问题比较突出。

functionPerson(){}Person.prototype={constructor:Person,name:"Brittany",friends:["pink","judy","sam"],age:23,job:"Webfront-endengineer",getName:function(){console.log(this.name);}};varperson1=newPerson();varperson2=newPerson();person1.friends.push("leo");console.log(person1.friends); //["pink","judy","sam","leo"]console.log(person2.friends); //["pink","judy","sam","leo"]console.log(Person.prototype.friends);//["pink","judy","sam","leo"]person1.age=35;console.log(person1.age);//35console.log(person2.age);//23console.log(Person.prototype.age);//23

person1的friends属性修改影响了person2的friends,但是person1的age属性修改并未影响person2的age属性。

原因在于:friends数组存在于Person.prototype中而非person1中,因此修改也会通过person2.friends(与person1.friends指向同一个数组)反映出来。而age属性在person1中也存在一份,修改的age属性只是修改person1中的,并不能修改Person.prototype中的age属性。

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

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。这样,每个实例都会有自己的一份实例属性的副本,但又同时共享着对方法的引用。

functionPerson(name,age,job){this.name=name;this.age=age;this.job=job;this.friends=["aa","bb","cc"];}Person.prototype={constructor:Person,sayName:function(){console.log(this.name);}};varperson1=newPerson("Brittany",23,"Webfront-endEngineer");person1.friends.push("dd");//["aa","bb","cc","dd"]console.log(person1.friends);varperson2=newPerson("Sam",26,"Webfront-endEngineer");console.log(person2.friends);//["aa","bb","cc"]console.log(person1.friends==person2.friends);//falseconsole.log(person1.sayName==person2.sayName);//true

时间:2014-10-21

地点:合肥

引用:《JavaScript高级程序设计》


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表