js中创建对象的方式有很多,尤其是基于原型的方式创建对象,是理解基于原型继承的基础。因此在这里汇总一下,并对各种方法的利弊进行总结和对比,不至于以后对这些概念有模糊。
简单方式创建
var o = new Object();
我们都知道,实际上在javascript中并没有所谓的类的概念。因此在创建对象时不能像面向对象语言那样,通过类A new出来一个a对象。但是javascript有一个特殊的Object。我们可以借助Object来new一个对象。
然后可以通过如下方式给对象增加属性或方法:
o.name="abc";
但是因为没有类的约束,这种方式创建出来的对象无法实现对象的重复利用,并且没有一种固定的约数,操作起来可能会出现这样或者那样的意想不到的问题。
有这样一个例子:
var a = new Object;
var b = new Object;
var c = new Object;
c[a]=a;
c[b]=b;
alert(c[a]===a); //输出什么
关于这个例子的具体解答详见 例子出处
工厂方法创建
直接看一个例子:
function createPerson(){
var o = new Object();
o.name = "abc";
o.age = 20;
return o;
}
这种创建对象的方法是在一个function中new Object(),并且赋予属性和方法,最后return 带有这些属性和方法的对象。
但是当我们想通过
var p1 = createPerson();
alert(typeof(p1));//Object 仅能得到这个结果,实际上没有太大意义
alert(p1 instanceof(类名???))//会发现其实并不存在一个Person类
构造函数的方式
function Person(name,age){
this.name = name;
this.age = age;
this.say = function(){
//
}
}
var p1 = new Person('abc',20);
构造函数的方式创建的对象:
函数名即为类名
通过this来定义属性
通过new Person()创建对象
并且有如下属性:
alert(typeof(p1));// Person
alert(p1 instanceof(Person));// true
-
但是同时我们也发现这种方式创建对象的一个弊端。对于类中的say方法,每分配一个对象就会有一个say的内存空间被分配出来。有一个say的拷贝。如果方法特别多的时候,会造成内存空间的极大浪费。可以通过两种方式进行优化和改进:
-
将say声明为全局的
function say(){ }
然后在类的定义中通过:
this.say = say;
采用下面提到的基于原型的方式创建对象
-
让类中的行为统一指向全局的say方法。但是如果将所有的方法设为全局的时候,就可以被window对象调用,那么就破坏了对象的封装性;如果方法很多,会造成代码中充斥着大量的全局函数。
基于原型创建对象
function Person(){
}
Person.prototype.name = "abc";
Person.prototype.age= 20;
Person.prototype.say= function(){
alert(this.name+this.age);
}
var p1 = new Person();
p1.say();//ok
say();//no 完成了封装
原型是js中的一个特殊对象。当一个函数创建之后,会随之产生一个原型对象。该函数中的prototype属性指向该源性对象;
当通过该函数的构造函数创建一个具体对象时,在这个对象中,就会有一个_prop_属性指向原型。这些是js中的很重要的一种继承方式--基于原型的继承的基础。这里不再赘述。
基于原型创建对象时,如果对象的属性和方法特别多时,可以通过如下方式进行定义:
Person.prototype = {
name:"abc",
age:20,
say:function(){
}
}
称为原型重写。原型重写之后当我们再通过 var p1 = new Person()创建一个对象时,p1 的constructor != Person()了。由于原型重写了,而且没有通过prototype指向,从而指向了Object()。
如果constructor比较重要,可以再json格式的定义中手动制定
constructor:Pserson
关于原型重写,我画了个示意图,比较容易理解:
p1是原型重写前声明的对象,p2是原型重写
Person.prototype.name = "123";
之后的声明的对象。
可以看出
constructor的指向确实没有自动变换,除非通过上述手动的方式进行修改。
通过p2.name = "456",设置name时,会在自己的存储空间中存储。当然查找name属性时,也是从自己的内存空间中读取name值。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。