首頁  >  文章  >  web前端  >  詳解JavaScript中的屬性和特性

詳解JavaScript中的屬性和特性

黄舟
黄舟原創
2016-12-13 16:57:551098瀏覽

JavaScript中屬性和特性是完全不同的兩個概念,這裡我將根據自己所學,來深入理解JavaScript中的屬性和特性。

主要內容如下:

理解JavaScript中物件的本質、物件與類別的關係、物件與引用類型的關係

物件屬性如何進行分類

屬性中特性的理解 

第一部分:理解Java物件的本質、物件與類別的關係、物件與參考類型的關係

物件的本質:ECMA-262把物件定義為:無序屬性的集合,其屬性可以包含基本值、物件或函數。即物件是一組沒有特定順序的值,物件的每個屬性或方法都有一個名字,而這個名字都映射到一個值。故物件的本質是一個散列表:其中是一組名值對,值可以是資料或函數。

物件和類別的關係:在JavaScript中,物件和類別沒有任何關係。這是因為ECMAScript中根本沒有類別的概念,它的物件與其他基於類別的語言中的物件是不同的。

物件和引用類型的關係:物件和引用類型並不是等價的,因為每個物件都是基於一個引用類型建立的。

第二部分:對象屬性如何進行分類

由構造函數或對象字面量方法創建的對像中具有屬性和方法(只要提到屬性和方法,它們一定是屬於對象的;只要提到對象,它一定是具有屬性和方法的(自訂除外)),其中屬性又可分為資料屬性和存取器屬性,他們的區別如下:

資料屬性一般用於儲存資料數值,存取器屬性不包含資料值

訪問器屬性多用於get/set操作

第三部分:屬性中特性的理解

ECMAScript為了描述物件屬性(property)的各種特徵,定義了特性(attribute)這個概念。也就是說特性不同於屬性,特性是為了描述屬性的。下面,我將分別講解:

資料屬性及其特性

存取器屬性及其特性

如何利用Object.defineProperties()方法定義多個特性

如何利用Object.getOwnPropertyDescripter()方法讀取屬性的描述符以讀取屬性的特性

1.資料屬性及其特性

剛剛我們說過,資料屬性是用於儲存資料數值的,因此資料屬性具有一個資料值的位置,在這個位置可以讀取和寫入值。資料屬性有4個描述其行為的特性,由於ECMAScript規定:在JavaScript中不能直接存取屬性的特性(注意:不是不能存取),所以我們把它放在兩組方括號中。如下:

[[Configurable]]:預設值為true,a、表示能否透過delete刪除屬性從而重新定義屬性 b、能否修改屬性的特性 c、能夠把屬性由資料屬性修改為存取器屬性

[[Enumerable]]:預設值為true,表示能否透過for-in循環回傳該屬性(所以:如果為false,那麼for-in循環沒法枚舉它所在的屬性)

[[Writable]]:預設值為true,表示能否修改屬性的值,這是與[[Configurable]]不同之處。

[[Value]]:預設值為undefined,這個值即為屬性的屬性值,我們可以在這個位置上讀取屬性值,也可以在這個位置上寫入屬性值。

注意:上述的預設是指透過建構函式或物件字面量所建立的物件所自行擁有的屬性,而不是下面要介紹的Object.defineProperty()方法

這些特性都具有預設值,但是如果這些預設數值不是我們想要的,該怎麼辦呢?當然就是修改啦!我們可以透過Object.defineProperty()方法來修改屬性預設的特性。英文difineProperty為定義屬性的意思。這個方法接收三個參數:屬性所在的物件、屬性的名字、一個描述符物件。其中第三個參數描述符物件是物件字面量的方法所建立的,裡面的屬性和屬性值實際上保存的是要修改的特性和特性值。

下面透過幾個例子來深入理解。

a

var person={};
Object.defineProperty(person,"name",{
  writable:false,
  value:"zhuzhenwei"
});
console.log(person.name);//zhuzhenwei
person.name="heting";
console.log(person.name);//zhuzhenwei

這裡我用對象字面量的方法創建了一個對象,但是沒有同時創建方法和屬性。而是利用了Object.defineProperty()方法來創建了屬性和修改了預設值。這裡將writable設定為false,於是後面我試著修改person.name時,是無效的。


b

var person={};
Object.defineProperty(person,"name",{
  value:"zhuzhenwei"
});
console.log(person.name);//zhuzhenwei
person.name="heting";
console.log(person.name);//zhuzhenwei

注意看這個例子,這個例子中我刪去了writable:false,為什麼還是不能修改呢?這是因為之前我在介紹特性時,前三個預設為ture,是在建立物件並建立屬性的情況下得到的。對於透過呼叫Object.defineProperty()方法所建立的屬性,其前三個特性的預設值均為false,這裡需要注意。


c

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

這裡我們將新建的屬性name的特性設定為了configurable:false;因此下面刪除屬性的操作是無效的。根據b,可知configurable,預設就是false,即使去掉也不可修改。


d

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

在这里我将默认的configurable的值由默认的false修改为了true,于是变成了可配置的,那么最后就成功删除了。

e

var person={};
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
  configurable:false
});
console.log(person.name);//zhuzhenwei
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
  configurable:true
});
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)

如果之前已经设置成为了false,那么后面再改成true也是徒劳的,即:一旦把属性设置成为不可配置的,就不能再把它变回可配置了。


f

console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)
var person={};
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
});
console.log(person.name);//zhuzhenwei
Object.defineProperty(person,"name",{
  value:"zhuzhenwei",
  configurable:true
});
console.log(person.name);//Uncaught TypeError: Cannot redefine property: name(…)

   

这里可以说明,即使前一步我们不管默认的configurable:false,后面得到的仍是不可配置。于是,可以得出结论,为了可配置,必须在第一次调用Object.defineProperty()函数时就将默认的值修改为true。

2.访问器属性及其特性  

之前提到,访问器属性不包含数据值,他们包含一对getter函数和setter函数(这两个函数不是必须的)。在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性是,会调用setter函数并传入新值,这个函数负责决定如何处理数据。同样,由于不能通过JavaScript来直接访问得到访问器属性的特性,所以下面列出的特性将由[[]]括起来以作区分。

[[Configurable]]:默认值为true,a、表示能否通过delete删除属性从而重新定义属性 b、能否修改属性的特性 c、能够把属性由访问器属性修改为数据属性

[[Enumerable]]:默认值为true,表示能否通过for-in循环返回该属性(所以:如果为false,那么for-in循环没法枚举它所在的属性)

[[Get]]:在读取属性时调用的函数。默认值为undefined  关键:特性可以是一个函数

[[Set]]:  在写入属性时调用的函数。默认值为undefined 关键:特性可以是一个函数 由于get和set函数也属于属性的特性,那么他们就有可能(说有可能是因为这两个函数也不是必须的)出现在Object.defineproperty的第三个参数描述符对象的属性中。

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

   2.访问器属性不能直接定义,必须是用Object.defineProperty()来定义。(通过这个规定我们就能准确地判断出访问器属性和数据属性了)

通过下面这个例子来深入理解:

var book={
  _year:2004,
  edition:1
};
Object.defineProperty(book,"year",{
  get:function(){<br>            return this._year;
  },
  set:function(newValue){
    if(newValue>2004){
      this._year=newValue;
      this.edition+=newValue-2004;
    }
  }
});
book.year=2005;
console.log(book.edition);//2

   

几个需要深入理解的地方:

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

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

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

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

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


3.如何利用Object.defineProperties()方法定义多个特性

显然,一个对象不可能只具有一个属性,因此,定义多个属性的可能性很大,于是JavaScript提供了Object.defineProperties()方法解决这个问题。这个方法接收两个参数,第一个是要定义属性所在的对象,第二个是一个对象字面量方法创建的对象,对象的属性名即为要定义的特姓名,对象的属性值又是一个对象,这个对象里的属性名和属性值分别是特性名和特性值(这里不是很好理解,看例子即可)。

var book={};
Object.defineProperties(book,{
  _year:{
    writable:true,
    value:2004
  },
  edition:{
    writable:true,
    value:1
  },
  year:{
    get:function(){
      return this._year;
    },
    set:function(){
      if(newValue>2004){
        this._year=newValue;
        this.edition+=newValue-2004;
      }
    }
  }
}); 

4.如何利用Object.getOwnPropertyDescripter()方法读取属性的描述符以读取属性的特性

  我们可以使用Object.getOwnPropertyDescripter()方法来取得给定属性的描述符。getOwnPropertyDescripter即为取得自身属性描述符的意思。这个方法接收两个参数:属性所在的对象要要读取其描述符的属性名称。返回一个对象。

  对于访问器属性而言,这个对象的属性有configurable、enumerable、get和set;

  对于数据属性而言,这个对象的属性有configurable、enumerable、writable和value。

var book={};
Object.defineProperties(book,{
  _year:{
    value:2004
  },
  edition:{
    value:1
  },
  year:{
    get:function(){
      return this._year;
    },
    set:function(){
      if(newValue>2004){
        this._year=newValue;
        this.edition+=newValue-2004;
      }
    }
  }
});
var descriptor=Object.getOwnPropertyDescriptor(book,"_year");
console.log(descriptor.value);//2004
console.log(descriptor.configurable);//false 因为通过Object.defineProperties()方法创建的属性的特性configurable enumerable都是false
console.log(typeof descriptor.get);//undefined 注意:这是数据属性,是不具有get特性的
var descriptor=Object.getOwnPropertyDescriptor(book,"year");
console.log(descriptor.value);//undefined
console.log(descriptor.enumerable);//false
console.log(typeof descriptor.get);//function get虽然是属性的一个特性,但是它也是函数

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时想要关注更多的相关内容请关注PHP中文网(www.php.cn)!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn