JS-面向对象的程序设计

今天重读了一下<<JavaScript高级程序设计>>的几章,其中的面向对象的程序设计,写的很好,让自己受益匪浅,特此记录.

ECMAScript中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同.ECMA-262将对象定义为:"无序属性的集合,其属性可以包含基本值,对象和函数".

ECMAScript中有二种属性:数据属性和访问器属性.为了表示这些特性是内部值,规范把他们放在了二对方括号中如[[ Enmerable]]

1:数据属性,数据属性包含一个数值的位置,在这个位置可以读取和写入值,数据属性有4个描述行为的特性.

[[Configurable]] :表示能否通过delete删除属性从而从新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为true

[[Enumerable]] : 表示能否通过for-in循环返回属性,默认为true

[[Writable]] :表示能否修改属性的值,默认true

[[Value]] : 包含这个属性的值,读取属性值的时候,从这个位置读,写入属性值得时候,把新值保存在这个位置.默认undefined


例子:

var person={};

Object.defineProperty(person,"name",{

writable:false,

value:'Nicholas'

})

alert(person.name) //Nicholas

person.name="Tom"

alert(person.name) //Nicholas

由于Person的数据属性writable为false 所以不允许修改其值,即使赋新值也会返回旧值,严格模式下报错,使用delete 删除person.name也是返回旧值.

一旦将属性设置为不可配置就不能再变回可配置了,

2:访问器属性,访问器属性不包含数据值,他们包含一对getter和setter函数,在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值,在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据,

[[Configurable]]  : 表示能否通过delete删除属性从而从新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性,默认true.

[[Enumerable]]   : 表示能否通过for-in循环返回属性,默认true.

[[Get]] : 在读取属性时调用的函数,默认undefined

[[Set]]  : 在写入属性时调用的函数,默认undefined


var book={

_year:2004,

edition:1

}

Object.defineProperty(book,"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

book对象的_yaer 带有下横线,表示内部属性,在book的访问器属性year则包含一个getter 和setter函数,这个使用访问器属性的常见方法,Vue的监听对象变化就是用的这一属性.

定义多个属性:

var boo={};

Object.defindeProperties(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

}

}

}

});

以上例子即使同时定义对象的多个属性,

读取属性的特性,

使用Object.getOwnPropertyDescriptor()方法可以取得给定属性的描述符,接受二个参数,属性所在的对象,要读取器描述符的属性名称,,返回一个对象,包含configurable,enumerable,get 和set 如果是数据属性,返回configurable,enumerable,writable,value

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


工厂模式
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;
}
工厂模式虽然解决了创建多个相似对象的问题,但没有解决对象识别的问题,


构造函数模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
}
}


var person1=new Person('Tom',25,'Doctor');
与工厂模式相比,没有显式创建对象,直接将属性和方法赋给this对象,没有return语句
使用new创建的实例,constructor属性指向Person 可以用来检查数据类型,但使用instanceof 操作符更可靠, 创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型,而这是构造函数模式胜过工厂模式的地方.
使用构造函数的主要问题就是每个方法都要在每个实例上重新创建一遍.


原型模式 我们创建的每个函数都有一个prototype(原型)属性 这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法. 按照字面意思理解,prototype就是通过调用构造函数而创建的那个对象实例的原型对象.
function Person(){

}
Person.prototype.name="Tom"
Person.prototype.age=25
Person.prototype.job="Doctor"
Person.prototype.sayName=function(){
alert(this.name)
}
var person1=new Person();



理解原型对象
只要创建一个新的函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象,在默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针.
从本质上讲,如果[[Prototype]]指向调用isPrototypeOf()方法的对象那么这个方法就返回true.
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性.换句话说,添加这个是属性只会阻止我们访问原型中的那个属性,但不会修改那个属性.使用hasOwnProperty()方法可以检测一个属性市场存在实例中,还是存在原型中.
有两种方式使用in操作符:单独使用和在for-in 循环中使用.单独使用时in操作符会在通过对象能够访问给定属性时返回true,无论属性存在于实例还是原型中.
如果你想要获取所有实例属性,无论是否枚举,可以使用Object.getOwnPropertyNames()
更简单的原型语法
function Person(){}
Person.prototype={
name:'Tom',
age:25,
job:'Doctor',
sayName:function(){
alert(this.name)
}
}
这种简便的原型写法,constructor属性不在指向Person了,可以显式地将原型的constructor设置为本身,但是这种写法会使constructor的属性[[Enumerable]] 特性被设置为true,
原型的动态性表明你可以随时向原型中添加属性,属性会立即反应到实例中,但是如果重写原型对象,就会切断原型与实例的联系,导致报错.
原型对象的问题在于由共享的本性导致的.如果原型的属性值为对象的话,那么所有实例的这个属性都指向同一个属性,统一修改.


未完待续.....

JS-面向对象的程序设计