js面向对象程序设计之构造函数

再上一篇的开头说了创建对象的两种方式,一种是Object构造函数的方式,一种是对象字面量的方法。但这些方式创建多个对象的时候都会产生大量的重复代码。经过技术的进步也演化出来许多的创建对象的模式。本章会介绍 工厂模式,原型模式,构造函数模式和构造函数与原型模式的混合使用。

1,工厂模式

工厂模式是一个比较广为人知的模式,这种模式将细节抽象出来。代码如下

function createPerson(name,age,job){
    var o =new Object();
    o.name=name;
    o.age=age;
    o.job=job;

    o.sayName=function(){
        alert(this.name)
    }
    return o;
}

var person1=createPerson("ds",12,"dada");
var person2=createPerson("ds2",122,"dada2");

给他所需要的材料,他就会返回一个对象。解决了大量重复代码的问题。可是又有一个问题,就是每个返回的对象都是Object。每个对象的的类型部分却别开来。所以有个新的模式来解决这个问题。

2,构造函数模式

废话不多说,还是先上代码吧。

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=function(){
        alert(this.name)
    }
}

var person1=new Person("ds",12,"dada");
var person2=new Person("ds2",122,"dada2");

第二种的使用了new的关键字。实际上经历了4步。1,创建了一个新对象。2,将构造函数的作用域赋给创建的变量。3,执行代码。4,返回新的对象。

person1和person2是不同的实例。两个对像有一个Constructor(构造函数)指向Person的函数本身。而Constructor就是用来表示对象的类型。这也是构造函数模式和工厂模式的不同。而且想Object和Array的就是js中原生的构造函数。创建自定义的构造函数意味着可以为他的实例标识为一种特定的类型。

其实构造函数也是函数知识调用的方式不同而已。构造函数也可以普通的方式调用。

var p=new Object();
Person.call(p,"sss",22,"222");

但是构造函数并不是没有问题,比如说上面的那个SayName(),其实person1和person2的SayName其实并不是同一个Funcition的实例。也就是说每new一个对象,SayName本身的方法也被new了一次。所有实例的SayName都是独立的并没有指向同一个方法。解决的方法也有可以将方法提取到全局变量。

function Person(name,age,job){
    this.name=name;
    this.age=age;
    this.job=job;
    this.sayName=sayName;
}

function sayName()
{
    alert(this.name)
}

但是问题又来了,sayName作为全局变量的话却只能被Person调用,有点不太符合全局变量的定义。重要的是如果方法很多的话又要在全局变量中添加许许多多的方法。好在这些问题都可以通过原型模式来解决

3,原型模式

在说原型模型的时候,应该先说明一下prototype(原型)是什么?其实它就是一个指针,指向一个对象。这个对象就是可以给特定类型的所有实例共享的属性和方法。还是看代码吧。

function Person(){
}

Person.prototype.name="shaoqi";
Person.prototype.age=28;
Person.prototype.job="IT";
Person.prototype.sayName=function(){
    alert(this.name);
}

var person1=new Pserson();
person1.sayName();

上图解释了对象,实例,原型和构造函数之间的关系。

在js中,只要创建一个新函数就会为该函数创建一个prototype的属性。指向函数的原型对象。原型对象有都会有个constructor的指针,指向原函数。每个实例也有个指针[[prototype]],指向原型。其实每个实例和构造函数是没有直接关系的,是通过原型将实例和构造函数关联起来。

其实构造函数还有个更简单的写法。

function Person(){

}

Person.prototype={
    name:"shaoqi",
    age:23,
    job:"It",
    SayName:function(){
        alert(this.name);
    }
}

但这样又有个问题,这样就相当与重写了整个prototype,js本身是不会给它生成constructor的。也就是说这个原型对象中没有指向Person的指针。但可以给它显示的给它赋值。

function Person(){

}

Person.prototype={
    constructor:Person,
    name:"shaoqi",
    age:23,
    job:"It",
    SayName:function(){
        alert(this.name);
    }
}

但还有个小问题,就是当这样显示的给原型指定构造函数的话,它的[[Enumerable]]会默认为true,原生的constructor是不能被枚举的。如果一定要兼容ECMAScript5的话可以改写成下面代码

function Person(){

}

Person.prototype={
    name:"shaoqi",
    age:23,
    job:"It",
    SayName:function(){
        alert(this.name);
    }
}

Object.defineProperty(Perosn.prototype,"constructor",{
    enumerable:false,
    value:Person
})

defineProperty这个方法的话在上一遍中有提到过。用来给属性显示的赋值特性的方法。

原型模式还有个动态性,应为本身在实例中它是已指针的形式存在的。可以动态的给原型添加方法和属性。可以动态的添加到实例上面。当然原型对象也不是完美的,也是存在问题的。下面来看一段代码。

function Person(){

}

Person.prototype={
    constructor:Person,
    name:"shaoqi",
    age:23,
    job:"It",
    friends:["11","22","33"],
    SayName:function(){
        alert(this.name);
    }
}

var person1=new Person();
var person2=new Person();

person1.friends.push("44");
alert(person1.friends);
alert(person2.friends);

问题就在于所有的实例(person1,person2)的指针都指向了一个数组。person1对数组操作之后,会改变原型中的数据,那么person2的Friend也就变了。实际实例一般都是要有属于自己的全部属性的。所以这才是很少有人单独使用原型模式的原因。

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

其实看到这里,我们会发现原型模式和构造模式的一些问题。构造函数模式是所有的属性都是为每一个实例重新的初始化一个出来。而原型模式则是为所有的实例公用一个属性或者方法。其实两种方法都有点极端,在一个对象中,其实情况是多样的,有些属性需要独立,有些需要共享。所有就有了这种组合模式的出现。可谓是取了两种模式之长。

function Person(name,age,job)
{
    this.name=name;
    this.age=age;
    this.job=job;
    this.friends=["11","22"]
}

Person.prototype={
    constructor:Person,
    sayName:function(){
        alert(this.name);
    }
}

将需要独立出来属性放在构造函数里面进行初始化,然后类似方法的属性则通过原型的方式进行赋值。

这是在ECMAScript5中使用最广泛的创建自定义类型方式。

5,动态原型模式

其实在对象语言中如C#中是没有原型一说的,只有一个构造函数。为了尽量的消除这点差异便又来动态原型模式。它就是将所有的信息都封装到了构造函数中。

function Person(name,age,job)
{
    this.name=name;
    this.age=age;
    this.job=job;
    this.friends=["11","22"];
    if(typeof this.sayName != "function")
    {
        Person.prototype.sayName=function(){
            alert(this.name)
        }
    }
}

不同之处就是在构造函数中有个判断。这个判断保证里面的代码只在初始化的时候运行一遍。这样就可用同一个构造函数来完成组合模式的作用了。

6,寄生模式

这是一个比较少用的模式。其实他和工厂方式没什么太大的区别。看个例子。

function Person(name,age,job){
    var o =new Object();
    o.name=name;
    o.age=age;
    o.job=job;

    o.sayName=function(){
        alert(this.name)
    }
    return o;
}

var person1=new Person("dd",12,"dd");

是不是和工厂模式一毛一样??其实唯一的区别就在于调用的时候,寄生模式使用了new的方法进行初始化。这种模式一般使用在什么场景呢?

假设我们想在原生的Array的基础上改造一下便可以这样

function SpecialArray()
{
    var value=new Array();

    value.push.apply(values,arguments);

    value.toPopedString=function(){
        return this.join("|");
    }

    return value;
}

var colors=new SpecialArray("11","2","33");
alert(colors.toPopedString());

在原有的类型上进行改造,可以用到寄生模式,有点类似于c#中的扩展。但是这样产生的实例和构造函数,原型是没有任何关系的。所以建议在可以使用它模式的前提下就不要用这种模式了。

这一篇吧对象的构造函数说完了,也说了原型的原理。下一篇就开始继承和原型链了。

时间: 2024-10-08 17:33:34

js面向对象程序设计之构造函数的相关文章

js面向对象编程(二) 构造函数继承

构造函数绑定 //基类建筑物var building = function () {    this.spec = "building";}; //address:房子地址,toward:房子朝向var house = function (address, toward) {    this.address = address;    this.toward = toward;}; //使房子继承建筑物 //使用call或者apply方法,将父对象的构造函数绑定在子对象上,在子对象构造

js面向对象程序设计之继承

在面向对象语言中继承分成两种:接口继承和实现继承.解释一下,接口继承只继承方法的签名,而实现继承则是继承实际的方法.但是ECMAScript中的函数没有签名所以无法进行接口继承,只能是实现实现继承.而实现继承通常是依赖与原型链的. 原型链继承 原型的概念已经在上一篇中说过了.还是来简单的回顾一下构造函数,原型和实例的关系:每个构造函数都有一个圆形对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针.但是如果让一个构造函数的原型指针指向另一个对象.那么这时的原型对象将

JS面向对象(1)——构造函数模式和原型模式

1.构造函数模式 构造函数用来创建特定的类型的对象.如下所示: function Person(name,age,job){ this.name=name; this.job=job; this.age=age; this.sayName=function(){ alert(this.name); }; } var person1=new Person('nick',20,'student');var person2=new Person('nick',20,'student');alert(p

JS面向对象的程序设计 - 高设

1 //工厂模式 2 function createPerson (name, age, job){ 3 var o = new Object(); 4 o.name = name; 5 o.age = age; 6 o.job = job; 7 8 o.sayName = function (){ 9 alert(this.name); 10 } 11 return o; 12 } 13 14 var sam = createPerson("sam", 18, "web f

js面向对象小结(工厂模式,构造函数,原型方法,继承)

最近过了一遍尼古拉斯泽卡斯的高级程序设计第三版(红皮书)第六章:面向对象程序设计,现在把总结出来的东西和大家分享一下. 主要内容如下: 1.工厂模式 2.构造函数模式 3.原型模式 4.继承 一.工厂模式 工厂模式中的函数中会创建一个对象,最后return这个对象,通过每次调用时传入的参数不同来解决创建多个相似对象的问题. // 工厂模式 function creatPerson(name, age, job) { var o = {}; o.name = name; o.age = age;

JS面向对象(2)——原型链

原型链用于ECMAScript的继承.其思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.说人话,我们知道,一个构造函数Subtype,其原型对象有一个指向构造函数的指针,这是联系构造函数和原型的桥梁,如果我让原型对象的指针指向了另一个原型,而另一个原型原本指向其构造函数的指针也指向了另一个原型,那么,这就是一个原型链了,原型上的属性会一层一层往下传递. function SuperType(){ this.property=true; } SuperType.prototype.ge

js面向对象之:创建对象

最近在学习<js高级程序设计>,之前所接触的很多的js类库和jQuery插件都会用面向对象的方式来设计,而自己却还是停留在面向方法的阶段,所以今天好好记录一下学习的js创建对象. 第一种方式:工厂模式 function createPerson(name,age,job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; ret

javascript面向对象程序设计——封装(by vczero)

在javascript中,我们宣称是面向对象的程序设计.其实面向对象是js与生俱来的特性,每一门OOP语言都有自己的特性,我们不能为了OOP,去模仿另一门语言,把握语言的特性才是正道.我们可以通过new Object()来创建一个实例,但是不推荐这种形式,我们需要更为直观的数据封装. 一.封装你的数据特性 1 var user = { 2 name: '', 3 email: '' 4 }; 很简单,我们封装了一个JSON格式的对象,也称为字面量对象:User对象包含两个属性name和email

【JavaScript】 JS面向对象的模式与实践 (重点整治原型这个熊孩子 (/= _ =)/~┴┴ )

参考书籍 <JavaScript高级语言程序设计>—— Nicholas C.Zakas <你不知道的JavaScript>  —— KYLE SIMPSON 在JS的面向对象编程中,我们最为关注的是两种行为,一是创建对象,二是类继承 JS创建对象 构造函数模式创建对象 第一种创建对象的方式是构造函数模式 如下所示, 将构造函数中的属性和方法赋给一个新对象 /** * description: 构造函数模式创建对象 */ function Type (p) {   this.par