面向对象的程序设计
面向对象(Object-Oriented)是以类为概念,通过类可以创建各具无序属性(包含了基本值、对象、函数)的对象。在ECMA-262把对象定义为“无序属性的集合,其属性可以包含基本值、对象和函数。”,我们也可以把ECMAScript的对象想象成散列表。
理解对象
在java中对面向对象的理解,是一切皆为对象,对于这种语言把面向对象已做到极致。那么在JAVASCRIPT中跟它有一定的区别,比较传统的生成对象的方式是通过new一个类,如下代码
var dog = new Object();
dog.name = "Small Yellow";//一般家里的狗各种洋气的名字,我家的狗是土狗,就叫小黄了(哈哈)
dog.age = 5;
dog.bark = function(){
console.log("my name is "+this.name);
}
注意:
1. 对象的创建形式,通过关键字new
2. 对象的属性和函数赋值或者取值,以点的方式来操作
字面量创建对象,以函数式通过new创建的对象dog和字面量创建的dog是一样的,这个因人或场景而异,代码如下
var dog = {
name: "Small Yellow",
age: 5,
bark: function(){
console.log("my name is "+this.name);
}
}
属性类型
在对象内部才用的特性,描述了属性的各个特征,为了表示特性内部值,规范把其放在两队方括号中,如数据属性[[Configurable]],[[Enumerable]],[[Writable]],[[Value]]和访问器属性[[Configurable]],[[Enumerable]],[[Get]],[[Set]]:下面一一简单介绍一下
数据属性
数据属性包含了一个数据值的位置,在这个位置可以读取和写入值
- [[Configurable]]: 表示能否通过delete删除属性从而重新定义属性,或者能否把属性修改为访问器属性。像为dog对象定义的数据属性,它的这个特性默认值为true
- [[Enumerable]]:表示能否通过for-in循环返回属性。像为dog对象上定义的数据属性,它的这个特性默认为true
- [[Wriable]]:表示能否修改属性的值,像为dog对象上定义的数据属性,它的这个特性默认为true
- [[Value]]:包含这个属性的数据值,读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置上。而这个特性默认值为undefined
如下小例子介绍初始化对象后,数据属性的描述,具体参考代码和注释区域
var dog = {
name: "Small Yellow"//[[Configuration]]默认值为true,[[Enumerable]]默认值为true,[[Writable]]默认值为true,[[Value]]特性将被设置为"Small Yellow",而这个值的任何修改都会反应到这个位置
}
Object.defineProperty的使用
这个方法接收三个参数:属性所在的对象、属性的名字和一个属性描述符。主要是对于属性描述符进行讲解,在javascript中描述符(descriptor)对象的属性必须是:configurable,enumerable,writable,value。设置其中一个或多个值,可以修改对应的特性值。
对于上面的小例子,我们也可以向如下进行设置
var dog = {};//首先创建一个空对象
Object.defineProperty(dog, "name", {
writable:false,//[[writable]]设为false后,再赋值视为无效操作
value:"Small Yellow"
});
console.info(dog.name); // "Small Yellow"
dog.name = "Samll Dark";
console.info(dog.name); // "Small Yellow" 任然保持writable设置的初始值
注意:在严格模式下,如果属性为只读状态,强行再次赋值时,会抛出异常
继续探究configurable特性
当configurable为false时,此时表示不能从对象删除此属性和把数据属性改为访问器属性。
var dog = {};
Object.defineProperty(dog,"name",{
configurable:false,
value:"Small Yellow"
});
console.info(dog.name); //"Small Yellow"
delete dog.name;
console.info(dog.name); //"Small Yellow"
注意:在严格模式下,configurable为false时,如果强行删除对象的该属性时,那么辉抛出异常。而且,一旦把属性的configurable为false后,那么吃后悔药也没法了,它是不能够再次恢复为true了,如果强行设置为true,不但没有任何效果,而且会抛出异常。具体代码如下
var dog = {};
Object.defineProperty(dog,"name",{
configurable:false,
value:"Small Yellow"
});
//抛出异常,在chrome(其它浏览器只要支持es5规范,就ok,只是笔者习惯了chrome这种简约的浏览器,同时也推荐大家使用chrome浏览器进行技术探究)控制台可以直接观察到
Object.defineProperty(dog,"name",{
configurable:false,
value:"Small Yellow"
});
使用defineProperty方法时需注意几点
- 在调用defineProperty()方法时,如果不指定configurable、enumerable和writable,它们的默认值都为false。
- IE8是最先实现Object.defineProperty()方法的浏览器版本,但是其现实的不彻底,只能在DOM对象上使用这个方法,而且只能创建访问器属性,所以不推荐IE8来尝试。
访问器属性
在java或者c++这种面向对象的语言,访问器是其一大特色。但是JAVASCRIPT也会有相对应的访问器,但是与像java语言相比,存在很多不同之处
访问器属性不包含数据值,它们包含一对setter(设置函数)和getter(负责返回有效的值)函数,但这两个函数并不是必须存在的。访问器也会存在如下的4个特性
那么先上代码,紧接着会结合代码来讲访问器内部的4个特性
var codeVersion = {//代码管理
_year:2016,//代码入库时间
during:1//代码已经管理多少年
}
Object.defineProperty(codeVersion,"year",{
get: function(){
return this._year;
},
set: function(newYear){
if(newYear >= 2016){
this._year = newYear;
this.during += newYear - 2016;
}
}
});
codeVersion.year = 2017;
console.info(codeVersion.during);
- [[Configurable]]: 表示能否通过delete删除属性从而重新定义属性,或者能否把属性修改为访问器属性。像为codeVersion对象定义的访问器属性,它的这个特性默认值为true
- [[Enumerable]]:表示能否通过for-in循环返回属性。像为codeVersion对象定义的访问器属性,它的这个特性默认值为true
- [[Get]]:读取属性时调用的函数,默认值为undefined
- [[Set]]:写入属性时调用的函数,默认值为undefined
能定义一个属性,那么也能同时定义多个属性,下面来讲述一下defineProperties()方法的使用
此方法接受两个对象类型的参数,第一个参数对象是要添加或删除其属性的对象,第二个参数是要修改的属性组合的对象,具体格式如下面代码
var codeVersion = {};
Object.defineProperties(codeVersion,{
_year:{
configurable:true,
enumerable:true,
writable:true,
value:2016
},
during:{
writable:true,
value:1
},
year:{
get:function(){
return this._year;
},
set:function(newYear){
if(newYear >= 2016){
this._year = newYear;
this.during += newYear - 2016;
}
}
}
});
读取属性的特性
通过es5的Object.getOwnPropertyDescriptor()方法,可以获得到属性描述符对象,那么如果获取的是数据属性,那么返回的对象有configurable,enumerable,writable和value;如果获取的士访问器属性,那么返回的对象有configurable,enumerable,get和set
var codeVersion = {};
Object.defineProperties(codeVersion,{
_year:{
writable:true,
value:2016
},
during:{
writable:true,
value:1
},
year:{
get:function(){
return this._year;
},
set:function(newYear){
if(newYear >= 2016){
this._year = newYear;
this.during += newYear - 2016;
}
}
}
});
var descriptor = Object.getOwnPropertyDescriptor(book,"_year");
console.info("set type is "+typeof descriptor.set);//set type is undefined
console.info("get type is "+descriptor.get);//get type is undefined
console.info("value => "+descriptor.value);//value => 2016
console.info("configurable => "+descriptor.configurable);//configurable => false
console.info("writable => "+descriptor.writable);//writable => true
console.info("enumerable => "+descriptor.enumerable);//enumerable => false
var descriptor = Object.getOwnPropertyDescriptor(book,"year");
console.info("set type is "+typeof descriptor.set);//set type is function
console.info("get type is "+descriptor.get);//get type is function
console.info("value => "+descriptor.value);//vale => undefined
console.info("configurable =>"+descriptor.configurable);//configurable => false
console.info("writable => "+descriptor.writable);//writable => undefined
console.info("enumerable => "+descriptor.enumerable);//enumerable => false