JS对象

来源:互联网 发布:新疆如何绕过网络管制 编辑:程序博客网 时间:2024/06/07 01:11

理解对象

一、定义对象

定义对象有两种方法,一种是new关键字后跟Object构造函数:

var person = new Object();

person.name = "LiLei";

person.age = 20;

         另一种是对象字面量表示法:

var person = {

         name: "LiLei",

         age:20

};

         通常对象字面量方法是首选,它的封装方式更直观,也方便向函数传递大量可选参数。

二、属性类型

1、数据属性:数据属性包含一个数据值的位置,在这个位置可以读取和写入值。属性有四个特性:configurable(能否通过delete删除属性后重新定义,默认为ture)、enumerable(能否通过for-in循环返回属性,默认ture)、writable(能否重写属性,默认ture)、value(属性值,默认为undefine)。

         修改属性的这些默认特性,用Object.defineProperty()方法例如:

var person ={};

Object.defineProperty(person,"name",{

         writable: false,

         value: "LiLei"

});

2、访问器属性:访问器属性不包含数据的值,只包含一对儿getter和setter函数,访问器的属性:configurable(能否通过delete删除属性后重新定义,默认为ture)、enumerable(能否通过for-in循环返回属性,默认ture)、get(在读取属性时调用,默认为undefine)、set(在写入属性时调用,默认为undefine)。

         访问器属性不能直接定义,必须使用Object.defineProperty()方法,例如:

var book = {

         _year: 2016,

         edition: 1

};

Object.defineProperty(book,"year", {

         get: function (){

                   returnthis._year;

         },

         set: function (newValue){

                   if(newValue> 2004){

                            this._year= newValue;

                            this.edition= 2;

                   }

         }

});

只定义getter时表示setter不能写,反之亦然。

Object.defineProperties()可定义多个属性

var book = {};

Object.defineProperties(book,{

         _year: {

                   writable: false,

                   value: 2016

         },

         edition: {

                   writable: false,

                   value: 1

         },

         get: function (){

                   returnthis._year;

         },

         set: function (newValue){

                   if(newValue> 2004){

                            this._year= newValue;

                            this.edition= 2;

                   }

         },

});

         以上代码定义了两个数据属性(_year和edition)和一个访问器属性(year)。

3、Object.getOwnPropertyDescriptor()可取得给定属性的描述符

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

alert(descriptor.value);//2016

创建对象

一、工厂模式

考虑到JS中没有类,开发人员发明了一种函数,用函数来封装以特定接口创建对象的细节:

function creatPerson (name , age , job){

         varo = new Object();

         o.name= name;

         o.age= age;

         o.job= job;

         o.sayName= function(){

                   alert(this.name);

         }

         return o;

}

var p1 = new creatPerson("LiLei", 18 , "engineer");

var p2 = newcreatPerson("HanMei", 16 , "teacher");

二、构造函数模式

         可以创建自定义的构造函数,从而定义自定义对象的属性和方法。构造函数与普通函数的不同在于,没有返回值、直接用this赋值、不用显示的创建对象,此外构造函数的首字母大写,普通函数的首字母小写。

function Person(name , age , job) {

         this.name= name;

         this.age= age;

         this.jod= job;

         this.sayName= function(){

                   alert(this.name);

         }

}

var p1 = new Person("LiLei" , 18, "engineer");

var p2 = new Person("HanMei", 16, "teacher");

         构造函数的用法:

//当做构造函数时使用

var p1 = new Person("LiLei" , 18, "engineer");

p1.sayName();//LiLei

//当做普通函数使用

Person("HanMei", 16 ,"teacher");

window.sayName();//HanMei

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

var o = new Object();

Person.call(o,"LiLei" , 18 ,"engineer");

o.sayName();//LiLei

当在全局对象中调用一个函数时,this指向Global对象,在浏览器中就是Window对象。

         使用构造函数创建对象的弊端在于,在构造函数中定义方法的话,需要创建函数,这会导致不同的实例化对象各自创建自己的方法函数,实质上这些方法函数实现的是一个功能,却创建了很多个,导致资源浪费。而将此函数从构造函数中提取出来,做成全局函数,可保证各个实例对象使用一个方法函数,但这导致全局函数名不副实,太多这种名不副实的函数会使代码变得混乱。此弊端可以通过原型模式来解决。

三、原型模式

         我们创建的每个函数都有一个prototype(原型)属性,这个属性就是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。用一个极端的例子来说明原型能做什么:

function Person(){

}

Person.prototype.name = "LiLei";

Person.prototype.age = 18;

Person.prototype.job ="engineer";

Person.prototype.sayName = function (){

         alert(this.name);

}

var p1 = new Person();

p1.sayName();//LiLei

var p2 = new Person();

p2.sayName();//LiLei

alert(p1.sayName == p2.sayName);//ture

         虽然原型的功能很多,但我们常常仅使用原型来初始化函数,原因后面详解。

1、  理解原型对象

新创建任何函数,该函数都具有prototype属性,这个属性指向函数的原型对象,所有的原型对象都有一个默认的constructor属性,constructor属性指向prototype所在函数。创建构造函数后,原型对象默认只会取得constructor属性。

当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,换言之,添加这个属性只会阻止我们访问原型中的那个属性,并不会修改那个属性,及时把这个属性设置为null也不会恢复其指向原型的链接,只有使用delete完全删除实例属性,才能恢复链接。

         我们可以用isPrototypeOf()方法来检测属性来自于原型,还是来自于构造函数。ECMAScript5新增Object.getPrototypeOf属性来获取实例引用的原型。hasOwnProperty()方法可以检测一个属性是存在于实例中,还是原型中。hasPrototypeProperty()判断属性是否来自于原型。

2、  原型与in操作符

在单独使用时,in操作符会在通过对象能够访问给定属性时返回ture,无论该属性来自于原型还是实例。

         使用for-in循环时,返回的是所有的能够通过对象访问的、可枚举的属性,不论是存在于实例中的属性,还是存在于原型中的属性名。低版本的浏览器可能需要其他的方法来获取这些属性名。

3、  更简单的原型语法

functionPerson(){

}

Person.prototype= {

name : "LiLei",

age : 18,

job : "engineer",

sayName : function(){

           alert(this.name);

}

}

这个方法的弊端在于constructor属性变成了指向Object的函数,不再指向Person,解决这一问题的方法是在原型中声明一遍:

functionPerson(){

}

Person.prototype= {

constructor : Person,

name : "LiLei",

age : 18,

job : "engineer",

sayName : function(){

           alert(this.name);

}

}

4、  原型的动态性

实例与原型之间的连接是一个指针,而非一个副本,我们对原型所做的任何修改,都能在原型上立即体现出来,即便实例化在前,修改原型在后,例如:

var p1 = newPerson();

Person.prototype.sayName= function(){

alert("my name is "+this.name);

}

p1.sayName();//myname is LiLei

但是,如果重写整个原型对象:

Person.prototype= {

constructor : Person,

name : "LiLei",

age : 18,

job : "engineer",

sayName : function(){

           alert(this.name);

}

}

p1.sayName();//myname is LiLei

重写原型对象,切断了现有原型与任何之前已经存在的对象实例之间的关系,他们引用的仍然是最初的原型。

         另外,原生对象也可以采用原型的模式创建新的属性,但是不建议在产品化的程序中修改原生对象的原型。

         原型对象也有其问题,用原型定义基本类型的属性勉强可以,定义引用类型的属性很不合适,最好还是仅使用原型定义函数。

四、组合使用构造函数模式与原型模式

         构造函数模式适用于定义实例属性,而原型模式适用于定义方法和共享的属性。

五、动态原型模式

         面向对象开发时,看到独立的构造函数和原型可能比较困惑,动态原型模式把这些信息都封装到了构造函数中,在这里对原型所做的修改可以立即体现在实例中,所以这里不能使用字面量法,否则会覆盖原型。

functionPerson(name , age , job){

         //属性

         this.name= name;

         this.age= age;

         this.job= job;

         //方法

         if(typeof this.sayName != "function"){

                   Person.prototype.sayName= function(){

                            alert(this.name);

                   }

         }

         寄生构造函数模式和稳妥构造函数模式本文不再介绍。

本文是学习《JavaSceipt高级程序设计》的学习笔记,本文思想出自该书。

1 0
原创粉丝点击