javascript高级程序设计知识整理(3)

面向对象的程序设计

一、理解对象

1.数据属性(configurable,enumerable,writable,value

Object.defineProperty()方法:

作用:修改属性默认的特性

接收3个参数:属性所在对象,属性的名字,一个描述符对象

例如:

<span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="font-family:KaiTi_GB2312;font-size:14px;">var person={};
Object.defineProperty(person,"name",{  //name是数据属性,可以在这里修改它的4个默认值
<span style="white-space:pre">	</span>configurable:false,  //这里可以修改上述四个属性的值(true/false)
<span style="white-space:pre">	</span>value:"Nicholas"
});</span></span>

数据属性的值可以直接赋予,即,不通过Object.defineProperty()方法,例如:

var person={

name:"Nicholas"

};

2.访问器属性(configurable,enumerable,get,set)

注意:访问器属性不能直接定义,只能通过Object.defineProperty()定义。

举例:

<span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="font-family:KaiTi_GB2312;font-size:14px;">var book={
   _year:2004,
   edition:1
};
Object.defineProperty(book,"year",{ //year是一个访问器属性
   get:function(){
     return this._year; 
   },
   set:function(newValue){
     if(newValue>2004){
       this._year = newValue;
       this.edition += newValue-2004;
     }
   }
});
book.year=2005;
alert(book.edition); //2</span></span>
附:定义访问器的旧有方法:

<span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="font-family:KaiTi_GB2312;font-size:14px;">var book={
   _year:2004,
   edition:1
};
book._defineGetter_("year",function(){
    return this._year;
});
book._defineSetter_("year",function(newValue){
    if(newValue>2004){
        this._year=newValue;
        this.edition+=newValue-2004;
    }
});
book.year=2005;
alert(book.edition); //2</span></span>
3.定义多个属性

Object.defineProperties()方法:

接收两个参数:

要操作的对象、对象的属性

例如:

<span style="font-family:KaiTi_GB2312;font-size:14px;"><span style="font-family:KaiTi_GB2312;font-size:14px;">var book={};
Object.defineProperties(book,{
  _year:{ //数据属性
    value:2004
  },
  edition:{ //数据属性
   value:1
  },
  year:{  //访问器属性
     get:function(){
        return this._year; 
      },
     set:function(newValue){
        if(newValue>2004){
          this._year = newValue;
          this.edition += newValue-2004;
        }
      }
   }
});</span></span>
4.读取属性特性的方法

Object.getOwnPropertyDescriptor():

接收两个参数:

属性所在对象,属性名称

用法举例:

var descriptor=Object.getOwnPropertyDescriptor(book,"_year");

alert(descriptor.value); //可以访问到属性_year的value(value,configurable等特性)

二、创建对象

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("Nicholas",29,"software engineer");

var person2=createPerson("Greg",25,"doctor");

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("Nicholas",29,"software engineer");

var person2=new Person("Greg",25,"doctor");

这里,person1、person2是Person的两个不同的实例,可以理解为,Person是我们自定义的一个类,person1、person2是为这个类创建的两个对象。

两个对象都有一个constructor(构造函数)属性,该属性指向Person。即:person1.constructor==Person;

一些注意点:

构造函数的调用:

//当作构造函数调用

var person=new Person("Nicholas",29,"software engineer");

person.sayName();//"Nicholas"


//当作普通函数调用

Person("Greg",25,"doctor");//直接调用,默认添加到window(相当于把这里创建的Person的属性和方法都赋给了window对象)

window.sayName();//"Greg"


//在另一个对象的作用域中调用

var o=new Object();

Person.call(o,"Sherry",20,"student");

o.sayName();//"Sherry"

3.原型模式

说明:我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象(原型对象),包含着某个类型的所有实例共享的属性和方法。也就是说,我们可以不在构造函数中定义对象实例的信息,而是将这些信息添加到原型对象中。

代码说明:

function Person(){

}

Person.prototype.name="Nicholas";

Person.prototype.age=29;

Person.prototype.job="software engineer";

Person.prototype.sayName=function(){

  alert(this.name);

};

var person1=new Person();

person1.sayName(); //"Nicholas"

var person2=new Person();

person2.sayName(); //"Nicholas"


如果创建实例后,对原型对象的属性进行"修改",实例对象会自动给自己添加一个属性,浏览器访问时,会先检查实例对象自己的属性,如果找到了想要获得的属性的名称,则不会再去查找原型对象的属性,也就是,会屏蔽原型对象 相应的属性。例如:

person1.name="Sherry";

alert(person1.name); //"Sherry"--来自实例

alert(person2.name); //"Nicholas"--来自原型

若赋值后,又想删除实例属性,则用delete操作符:

delete person1.name;

alert(person1.name); //"Nicholas"


检测一个属性是实例属性还是原型属性:

hasOwnProperty():

alert(person1.hasOwnProperty("name")); //检查person1有没有名为name的实例属性(true/false)

检测一个属性是否存在于一个对象中(存在于实例中或实例的原型中皆可):

in操作符:

alert("name" in person1); //true(这里,就算person1没有name这个实例属性,也返回true,因为原型对象中包含name属性)


取得对象上所有可枚举的实例属性(实例对象,只返回实例属性;原型对象,返回原型属性):

Object.keys():

接受参数:对象(原型对象、实例对象皆可)

返回:该对象所有可枚举属性组成的字符串数组

取得对象上所有实例属性(无论是否可枚举):

getOwnPropertyNames():同样传入对象


更简单的原型语法:

function Person(){

}

Person.prototype={

  constructor:Person,

  name:"Nicholas",

  age:29,

  job:"software engineer",

  sayName:function(){

     alert(this.name);

  }

};

注意:这样相当于重写了默认的prototype对象,因此,这里的原型对象的constructor属性默认不再指向Person构造函数,而是指向Object构造函数。因此,如果需要constructor依然指向Person,需要显式设定,如代码红色部分。不过,这样的设定,会导致constructor的enumerable特性被设置为true(默认是false)


原型的动态性:

function Person(){

}

var friend=new Person();

Person.prototype={

  constructor:Person,

  name:"Nicholas",

  age:29,

  job:"software engineer",

  sayName:function(){

     alert(this.name);

  }

};

friend.sayName(); //error

解析:

在没有给Person()的原型对象添加属性和方法时,原型对象默认只有一个constructor属性,指向Person构造函数。这时,创建的实例对象friend,指向这个只有constructor属性的原型对象。而后,重写了Person的原型对象,如例子中这样的字面量写入法,相当于给Person创建了一个新的原型对象,在这之后创建的实例对象,都会指向这个新的原型对象,也就具有name这些属性了。但是,friend已经创建完毕,它指向的依然还是重写前的那个原型对象,因此,它不具有sayName()方法,发生错误。

4.构造函数模式+原型模式

function Person(name,age,job){

    this.name=name;

    this.age=age;

    this.job=job;

    this.friends=["Sherry","Court"];

}

Person.prototype={

    constructor:Person,

    sayName:function(){

        alert(this.name);

    }

}

var person1=new Person("Nicholas",29,"software engineer");

var person2=new Person("Greg",27,"doctor");

person1.friends.push("Van");

alert(person1.friends); //"Sherry,Court,Van"

alert(person2.friends); //"Sherry,Court"  注:如果是单纯的原型模式,这里返回值同上行,由原型对象的共享造成

5.动态原型模式

原理:通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型

function Person(name,age,job){

    this.name=name;

    this.age=age;

    this.job=job;

    if(typeof this.sayName!="function"){ //这里,只在sayName()方法不存在的情况下,才会将它添加到原型中。这段代码只在初次调用构造函数时会执行。

        Person.prototype.sayName=function(){

             alert(this.name);

        };

    }

}

var friend=new Person("Nicholas",29,"software engineer");

friend.sayName(); 

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 friend=new Person("Nicholas",29,"software engineer"); //注意这里用new调用,和工厂模式不同

friend.sayName(); //"Nicholas"

这个模式可以在特殊的情况下用来为对象创建构造函数:

function SpecialArray(){

    var values=new Array();

    values.push.apply(values,arguments);

    values.toPipedString=function(){

         return this.join("|");

    };

    return values;

}

var colors=new SpecialArray("red","blue","green");

alert(colors.toPipedString()); //"red|blue|green"

7.稳妥构造函数模式

function Person(name,age,job){

    var o=new Object();

    o.name=name;

    o.age=age;

    o.job=job;

    o.sayName=function(){

     alert(name); //这里与寄生构造函数不同

    };

    return o;

}

var friend=Person("Nicholas",29,"software engineer"); //这里不用new,与寄生构造函数不同

friend.sayName(); //"Nichoals"

这里,变量friend中保存的是一个稳妥对象,除了调用sayName()方法外,没有别的方式可以访问其数据成员,因此这种模式相对安全。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值