JavaScript之面向对象的属性和特性

来源:互联网 发布:高颜值历史名人 知乎 编辑:程序博客网 时间:2024/05/04 13:20

      对象的本质:ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。即对象是一组没有特定顺序的值,对象的每个属性或方法都有一个名字,而这个名字都映射到一个值。故对象的本质是一个散列表:其中是一组名值对,值可以是数据或函数。

     既然提到对象,那么我们需要先来看一下如何创建对象。创建自定义对象的最简单方式就是创建一个Object 的实例,然后再为它添加属性和方法。

var person = new Object();person.name = "Nicholas";person.age = 29;person.job = "Software Engineer";person.sayName = function(){    alert(this.name);};
     我们也可以采用字面量的方式进行创建对象,用对象字面量语法可以写成这样:

var person = {    name: "Nicholas",    age: 29,    job: "Software Engineer",    sayName: function(){      alert(this.name);  }};

      由构造函数或对象字面量方法创建的对象中具有属性和方法(只要提到属性和方法,它们一定是属于对象的;只要提到对象,它一定是具有属性和方法的(自定义除外)),ECMAScript 中有两种属性:数据属性访问器属性,他们的区别如下:

  • 数据属性一般用于存储数据数值,访问器属性不包含数据值;
  • 访问器属性多用于get/set操作;

1、数据属性

     刚刚我们说过,数据属性是用于存储数据数值的,因此数据属性具有一个数据值的位置,在这个位置可以读取和写入值。数据属

性有4个描述其行为的特性,由于ECMAScript规定:在JavaScript中不能直接访问属性的特性(注意:不是不能访问),所以我们

把它放在两组方括号中。如下:

  • [[Configurable]]:表示能否通过delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
  • [[Enumerable]]:表示能否通过for-in 循环返回属性。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。
  • [[Writable]]:表示能否修改属性的值。像前面例子中那样直接在对象上定义的属性,它们的这个特性默认值为true。这是与[[Configurable]]不同之处。
  • [[Value]]:包含这个属性的数据值。读取属性值的时候,从这个位置读;写入属性值的时候,把新值保存在这个位置。这个特性的默认值为undefined。

       对于像前面例子中那样直接在对象上定义的属性,它们的[[Configurable]]、[[Enumerable]]和[[Writable]]特性都被设置为true,而[[Value]]特性被设置为指定的值。

       要修改属性默认的特性,必须使用ECMAScript 5 的Object.defineProperty()方法。这个方法接收三个参数:属性所在的对象、属性的名字和一个描述符对象。其中,描述符(descriptor)对象的属性必须是:configurable、enumerable、writable 和value。设置其中的一或多个值,可以修改对应的特性值。例如:

var person = {};Object.defineProperty(person, "name", {    writable: false,    value: "Wendy"});console.log(person.name); //"Wendy"person.name = "Kitty";console.log(person.name); //"Wendy",这个属性的值是不可修改的,如果尝试为它指定新值,则在非严格模式下,赋值操作将被忽略;在严格模式下,赋值操作将会导致抛出错误。
      这个例子创建了一个名为name 的属性,它的值"Wendy"是只读的。这个属性的值是不可修改的,如果尝试为它指定新值,则在非严格模式下,赋值操作将被忽略;在严格模式下,赋值操作将会导致抛出错误。

     再看另外一个例子。

var person={};Object.defineProperty(person,"name",{    value:"Wendy"});console.log(person.name);//Wendyperson.name="Kitty";console.log(person.name);//Wendy
     在这个例子中我删去了writable:false,为什么还是不能修改呢?这是因为configurable、enumerable、writable的三个默认为true,是在创建对象并创建属性的情况下得到的。对于通过调用Object.defineProperty()方法创建的属性,其前三个特性的默认值均为false。

    我们对configurable也进行设置,将其设置为不可配置,如下:

var person = {};Object.defineProperty(person, "name", {    configurable: false,    value: "Wendy"});console.log(person.name); //"Wendy"delete person.name;console.log(person.name); //"Wendy"

      把configurable 设置为false,表示不能从对象中删除属性。 如果对这个属性调用delete,则在非严格模式下什么也不会发生,而在严格模式下会导致错误。一旦把属性定义为不可配置的,就不能再把它变回可配置了。此时,再调用Object.defineProperty()方法修改除writable 之外的特性,都会导致错误:

var person = {};Object.defineProperty(person, "name", {    configurable: false,    value: "Wendy"});Object.defineProperty(person, "name", {    configurable: true,    value: "Wendy"});
     也就是说,可以多次调用Object.defineProperty()方法修改同一个属性,但在把configurable特性设置为false 之后就会有限制了。在调用Object.defineProperty()方法时,如果不指定,configurable、enumerable和writable特性的默认值都是false。多数情况下,可能都没有必要利用Object.defineProperty()方法提供的这些高级功能。

注意:

  • 一旦把属性设置成为不可配置的,就不能再把它变回可配置了。
  • 调用Object.defineProperty()方法创建的属性,其configurable、enumerable、writable三个特性的默认值均为false。
2、访问器属性

       访问器属性不包含数据值;它们包含一对getter 和setter 函数(不过,这两个函数都不是必需的)。在读取访问器属性时,会调用getter 函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter 函数并传入新值,这个函数负责决定如何处理数据。访问器属性有如下4 个特性。

  • [[Configurable]]:表示能否通过delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为数据属性。对于直接在对象上定义的属性,这个特性的默认值为true。
  • [[Enumerable]]:表示能否通过for-in 循环返回属性。对于直接在对象上定义的属性,这个特性的默认值为true。
  • [[Get]]:读取属性时调用的函数。默认值为undefined。
  • [[Set]]:写入属性时调用的函数。默认值为undefined。

     注意:

1)相对于数据属性,我们发现访问器属性中没有writable特性和value特性。这是因为访问器属性不包含数据值,那么我们当然就不可修改属性的值(用不到writable特性),更不用考虑value了。

2)访问器属性不能直接定义,必须使用Object.defineProperty()来定义。

看一下下面的例子。

var book = {_year:2005,edition:1};Object.defineProperty(book,"year",{get:function(){return this._year;},set:function(newyear){if(newyear > 2005){this._year = newyear;this.edition += newyear - 2005;}}});book.year = 2006;console.log(book.edition);

具体的解释

1)访问器属性不能直接定义,必须使用Object.defineProperty()来定义,且该属性具有set和ger特性,于是可以判断,_year和edition是数据属性,而year是访问器属性。

2)我们看到_year这个数据属性前面是以_(下划线)开头的,这个一种常用的记号,用于表示只能通过对象方法访问的属性从上面的例子中可以看到get相当于描述符对象的一个方法,而_year正是在这个对象方法访问的属性。而edition既可以通过对象方法访问,也可以由对象直接访问。

3)book.year表示正在读取访问器属性,这时会调用get函数,并返回了2005这个有效的值。

4)book.year=2006表示写入访问器属性,这时会调用set函数并传入新值,即将2006传给newValue,这个函数决定如何处理数据。

5)这时使用访问器属性的常见方法-即设置一个属性的值会导致其他属性发生变化。

      不一定非要同时指定getter 和setter。只指定getter 意味着属性是不能写,尝试写入属性会被忽略。在严格模式下,尝试写入只指定了getter 函数的属性会抛出错误。类似地,只指定setter 函数的属性也不能读,否则在非严格模式下会返回undefined,而在严格模式下会抛出错误。

      支持ECMAScript 5 的这个方法的浏览器有IE9+(IE8 只是部分实现)、Firefox 4+、Safari 5+、Opera12+ 和Chrome 。在这个方法之前, 要创建访问器属性, 一般都使用两个非标准的方法:__defineGetter__()和__defineSetter__()。这两个方法最初是由Firefox 引入的,后来Safari 3、Chrome 1 和Opera 9.5 也给出了相同的实现。




1 0
原创粉丝点击