ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP)。面向对象的语言有一个标志:类的概念,通过类可以创建任意多个具有相同属性和方法的对象。然而,JS中没有类的概念,因此它的对象和其他那些基于类的语言中的对象是不同的。
1、普通创建对象
创建一个对象,给这个对象新建属性和方法。
var demo=new Object(); //创建一个Object对象
demo.name="bert"; //创建一个name属性并赋值
demo.age=24; //创建一个age属性并赋值
demo.run=function(){ //创建一个run()方法并返回值
return this.age+"岁的"+this.name+"在跑步";
}
alert(demo.run()); //输出属性和方法的值
这是JS中创建对象最基本的方法,在run()方法里的this,就是代表demo对象本身。但是,这有一个问题,如果我想创建类似的对象,就会产生大量类似的代码。
譬如:
var demo1=new Object()
demo1.name="bert";
demo1.age=24;
demo1.run=function(){
return this.age+"岁的"+this.name+"在跑步";
}
alert(demo1.run());
var demo2=new Object()
demo2.name="Tom";
demo2.age=24;
demo2.run=function(){
return this.age+"岁的"+this.name+"在跑步";
}
alert(demo2.run());
var demo3=new Object()
demo3.name="Jack";
demo3.age=24;
demo3.run=function(){
return this.age+"岁的"+this.name+"在跑步";
}
alert(demo3.run());
2、工厂模式
为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法就是为了解决上述实例化对象产生大量重复的问题。
function createObject(Name,Age){ //集中实例化的函数
var demo=new Object();
demo.name=Name;
demo.age=Age;
demo.run=function(){
return this.age+"岁的"+this.name+"在跑步"
}
return demo;
}
var demo1=createObject("bert",24); //第一个实例
var demo2=createObject("Tom",23); //第二个实例
alert(demo1.run());
alert(demo2.run());
工厂模式就相当于创建一个模型,根据模子可以产生大量的对象实例,可这也出现一个问题,对象的类型都是相同的,那就无法区分某一个对象实例是由哪一个对象创建的。通俗理解,有两个工厂A、B,都生产一种相同的玩偶,玩偶是一模一样的,这就导致了根据玩偶就无法知道该玩偶来自哪个工厂,出了问题也就不方便去定位那个工厂。
//接上述代码
alert(typeof demo1); //返回结果Object
alert(typeof demo2); //返回结果Object
alert(demo1 instanceof Object); //true
alert(demo2 instanceof Object); //true
//都是Object的对象实例
3、构造函数
亦可采用构造函数(构造方法)创建特定的对象,其对象类型是Object对象。构造函数的方法有一些规范:
(1)函数名和实例化构造名相同且大写,但非强制,这么写有助于区分构造函数和普通函数,
(2)通过构造函数创建实例化对象,必须使用new运算符。
function Demo(Name,Age){//创建一个对象,所有构造函数的对象都是Object
this.name=Name;//添加一个属性
this.age=Age;
this.run=function(){//添加一个方法
return this.age+"岁的"+this.name+"在跑步";
}
}
var demo1=new Demo("bert",24);//实例化
var demo2=new Demo("Tom",23);//实例化
alert(demo1.run());
alert(demo2.run());
4、各方式比较
(1)与工厂模式相比,构造函数
1、构造函数方法没有显式的创建对象(new Object()),后台自动创建var demo=new Object,
2、直接将属性和方法赋值给this对象,这里的this相当于Demo,
3、没有renturn语句,后台会自动返回。
使用构造函数的方法,能够解决重复实例化的问题,下例子又证明了可以解决对象识别的问题,
function Demo1(Name,Age){//创建一个对象,所有构造函数的对象都是Object
this.name=Name;//添加一个属性
this.age=Age;
this.run=function(){//添加一个方法
return this.age+"岁的"+this.name+"在跑步";
}
}
function Demo2(Name,Age){//创建一个对象,所有构造函数的对象都是Object
this.name=Name;//添加一个属性
this.age=Age;
this.run=function(){//添加一个方法
return this.age+"岁的"+this.name+"在跑步";
}
}
var demo1=new Demo1("bert",24);//实例化
var demo2=new Demo2("Tom",23);//实例化
alert(demo1 instanceof Demo1);//返回结果true
alert(demo2 instanceof Demo2);//返回结果true
(2)与普通函数相比,构造函数,
function Demo(Name,Age){
this.name=Name;
this.age=Age;
this.run=function(){
return this.age+"岁的"+this.name+"在学习";
}
}
alert(Demo("bert",24));//构造函数像普通函数一样调用时无效,必须使用new运算符
//构造函数的正确调用
var test=new Demo("bert",24);
alert(test.run());
(3)对象冒充调用
function Demo(Name,Age){
this.name=Name;
this.age=Age;
this.run=function(){
return this.age+"岁的"+this.name+"在学习";
}
}
var fake=new Object();
Demo.call(fake,"Tom",23); //对象冒充
alert(fake.run());
5、构造函数的问题
对于一个构造函数进行两个相同的实例化,可想而知它们的属性和方法的值都是相同的,然而实际上它们在方法上却是不同的,
function Demo(Name,Age){
this.name=Name;
this.age=Age;
this.run=function(){
return this.age+"岁的"+this.name+"在学习";
}
}
var test1=new Demo("bert",24);
var test2=new Demo("bert",24);
alert(test1.run==test2.run); //返回结果false
//因为它们比较的是引用地址,实例化后地址不同
根据上例可知,this.run=function()是引用类型,替换成this.run=new Function(),得到一样的效果,更加证明它们最终判断的是引用地址,唯一性。可通过构造函数外面绑定同一个函数的方法来保证引用地址的一致性,
function Demo(Name,Age){
this.name=Name;
this.age=Age;
this.run=Run;//此处要改变的是Run整个,不可以加()
}
function Run(){
return this.age+"岁的"+this.name+"在学习";//把构造函数的内部方法通过全局来实现引用地址一致
}
var test1=new Demo("bert",24);
var test2=new Demo("bert",24);
alert(test1.run==test2.run);//返回结果为true,引用地址相同了
然而,上例方法不太好,虽然使用了全局的函数Run()来解决了保证引用地址一致的问题,但这种方式又带来了一个新的问题,全局中的this在对象调用的时候是Demo本身,而当作普通函数调用的时候,this又代表window。