JavaScript对象的创建

一.最简单的两种创建方式

1.使用Object构造函数

var person =new Object();
person.name="cjh";
person.age=19;

2.对象字面量

var person={
  name :"cjh",
  age :19
}
需要注意的是,花括号内,成员与成员之间以逗号隔开,而最后一个成员后不能跟逗号,否则会在IE7及更早的版本中报错。成员名也可以直接使用字符串。其一大优点是可以向函数传递大量参数。如下
function show(arg){
var output="";
if(typeof arg=="string"){
  output+="name: "+"arg.name"+"\n";
}
if(typeof arg=="number"){
 output+="age: "+"arg.age"+"\n";
}
alert(output);
}
show({
 name: "cjh",
 age:19
});

二.更高级的创建方法

虽然可以用上述两种创建方式,但使用同一个接口创建很多对象时会产生大量代码。由于在ECMAScript中没有类的概念,开发人员发明了更为高效的几种方法来创建对象。

1.工厂模式

代码如下
function creat(name){
 var o=new Object();
 o.name=name;
 o.SayName=function(){
   alert(this.name);
};
 return o;
}

然而,工厂模式并不能解决对象的识别问题,无法知道一个对象的类型,于是又有了下面这种创建方式

2.构造函数模式

将上面的例子用构造函数模式重写
function Person(name){
 this.name=name;
 this.SayName=new function(){
  alert(this.name);
 };
}
var me=new Person("CJH");
这个构造函数里没有显式创造对象,而是直接将属性和方法赋值给this对象,也没有返回值。还应注意到构造函数的首字母应当大写以此与非构造函数相区分。
在使用构造函数创建对象时实际上经历了如下四个步骤
(1).创建一个新对象;
(2).将构造函数的作用域赋给新对象;
(3).执行构造函数中的代码;
(4).返回新对象
由构造函数生成的对象,都有一个constructor属性指向其构造函数Person,该对象既是Object的实例,又是Person的实例。


应当注意,构造函数也是函数,如果不通过new进行调用,也可以作为普通函数将属性添加到this中,亦可在另一个对象中调用。

那么这种创建方式有没有缺陷呢?当然有。在每次使用构造函数时,每一个方法都会被重新创建一遍,也就是说创建两个对象的o1和o2的相同方法实际上指的不是同一个函数。这个问题大可如下改进
function Person(name){
  this.name=name;
  this.SayName()=SayName;
}
function SayName(){
  alert(this.name);
}

可惜这种创建方式使创建对象的过程毫无封装性可言。于是,原型模式应运而生!

3.原型模式

使用构造函数创建的对象都有一个prototype属性,该属性是一个指针指向一个对象,即此对象的原型对象。直接贴代码
function Person(){}
  person.prototype.name="CJH";
  person.prototype.SayName=function(){
    alert(this.name);
};
var p1=new Person;
p1.SayName();//"CJH"
这种方法创建的对象共有相同的方法和同一组属性。

无论何时创建了一个新函数,都会为该函数创建一个prototype属性,这个属性指向函数的原型对象。默认下,所有prototype对象都会有一个constructor属性:指向prototype属性所在函数的指针,即上文中的构造函数。而使用构造函数创建对象实例的时候,会在实例中自动创建一个[[prototype]]指针,指向实例的原型对象。该指针不能被任何方式访问到。
我们可以用isPrototy()peOf方法来确认
alert(Person.prototype.isPrototypeOf(p1));//true

ECMAScript5增加了一个叫Object.getPrototypeOf()的方法可以返回[[prototype]]的值,例如
alert(Object.getPrototypeOf(p1).name)//"CJH"
虽然可以通过访问实例返回原型的属性值,但却不能通过实例改变原型的属性值。如果我们在实例中添加了一个属性,而该属性与原型中的属性重名,那么该属性会在这个实例里屏蔽掉原型中那个属性,如下
function Person(){}
Person.prototype.name="CJH";
var p1=new Person();
p1.name="CTC";
var p2=new Person();
alert(p1.name)//"CTC"
alert(p2.name)//"CJH"
使用hasOwnProperty()方法可以检测一个属性是否存在与实例中还是存在与原型中。这个方法在给定属性存在于对象实例时才返回true。
function Person(){}
Person.prototype.name="CJH";
var p1=new Person();
alert(p1.hasOwnproperty("name"));//false
p1.name="CTC";
alert(p1.hasOwnproperty("name"));//true

通过这个方法,属性存在于实例还是原型一目了然


更简单的原型语法

用原型模式创建对象时每个属性都要打一遍Person.prototype。为了减少不必要的代码,常用一个包含所有属性和方法的对象字面量来重写原型对象。
function(){}
Person.prototype={
 name:"CJH";
 SayName:function(){//};
}
可是,使用这种方式的话,constructor属性不再指向Person了。这种方法完全重新创建了原型对象,constructor对象也指向了Object。
我们可以在重构原型对象时添加“constructor:Person,”既可。
在原型修改或添加的属性后在所有实例中会对应发生响应。然而重构整个prototype对象的话,情况就不同了。因为重构之后切断了原型与构造函数之间的联系。

原型模式的缺点

省略传参过程会带来许多不便,但这不是最大的问题。当包含引用类型属性时,问题就突出了。创造的实例中的所有对应属性都指向同一引用。因此我们有了下面这种方式

4.原型+构造函数

这是JS中最常用的创建对象的方式了,简而言之就是将实例属性在构造函数中定义,将所有共有属性和方法在构造函数的prototype中进行定义,可以完美解决上文中原型模式的缺点。代码不贴了

这篇文章只是做一个粗略的介绍,而且还剩下动态原型模式,寄生构造函数模式,稳妥构造函数模式没有讲,天色已晚,以后补充。



阅读更多
文章标签: JS
个人分类: 笔记
上一篇c++简易100行贪吃蛇
下一篇LeetCode 292-Nim游戏
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭