JS基礎篇--JS中的可枚舉屬性與不可枚舉屬性以及擴展

在JavaScript中,對象的屬性分爲可枚舉和不可枚舉之分,它們是由屬性的enumerable值決定的。可枚舉性決定了這個屬性可否被for…in查找遍歷到。segmentfault

1、怎麼判斷屬性是否可枚舉

js中基本包裝類型的原型屬性是不可枚舉的,如Object, Array, Number等,若是你寫出這樣的代碼遍歷其中的屬性:數組

var num = new Number();
for(var pro in num) {
    console.log("num." + pro + " = " + num[pro]);
}

它的輸出結果會是空。這是由於Number中內置的屬性是不可枚舉的,因此不能被for…in訪問到。app

Object對象的propertyIsEnumerable()方法能夠判斷此對象是否包含某個屬性,而且這個屬性是否可枚舉。函數

須要注意的是:若是判斷的屬性存在於Object對象的原型內,無論它是否可枚舉都會返回false性能

2、枚舉性的做用

屬性的枚舉性會影響如下三個函數的結果:this

for…inspa

Object.keys()prototype

JSON.stringifycode

先看一個例子,按以下方法建立kxy對象:對象

function Person() {
    this.name = "KXY";
}
Person.prototype = {
    constructor: Person,
    job: "student",
};
 
var kxy = new Person();
Object.defineProperty(kxy, "sex", {
    value: "female",
    enumerable: false
});

其中用defineProperty爲對象定義了一個名爲」sex」的不可枚舉屬性

接下來作如下驗證:
a.

for(var pro in kxy) {
    console.log("kxy." + pro + " = " + kxy[pro]);
  }

結果:

kxy.name = KXY
kxy.constructor = function Person() {
    this.name = "KXY";
}
kxy.job = student

能夠看到除了」sex「以外的屬性都遍歷到了

b.

console.log(Object.keys(kxy));

返回結果:["name"]
只包含」name」屬性,說明該方法只能返回對象自己具備的可枚舉屬性。

c.

console.log(JSON.stringify(kxy));

返回結果:{"name":"KXY"}
此方法也只能讀取對象自己的可枚舉屬性,並序列化爲JSON字符串(經過typeof JSON.stringify(kxy)獲得string類型)。

Object.defineProperty()

上面用到了Object.defineProperty()方法,下面講解下。

語法

Object.defineProperty(object, propertyname, descriptor)

參數:

  • object:必需。 要在其上添加或修改屬性的對象。 這多是一個本機 JavaScript 對象(即用戶定義的對象或內置對象)或 DOM 對象。
  • propertyname:必需。 一個包含屬性名稱的字符串。
  • descriptor:必需。 屬性描述符。 它能夠針對數據屬性或訪問器屬性。

返回值:已修改對象。

備註:
可以使用 Object.defineProperty 函數來執行如下操做:

  • 向對象添加新屬性。 當對象不具備指定的屬性名稱時,發生此操做。
  • 修改現有屬性的特性。 當對象已具備指定的屬性名稱時,發成此操做。

描述符對象中會提供屬性定義,用於描述數據屬性或訪問器屬性的特性。 描述符對象是 Object.defineProperty 函數的參數。

若要向對象添加多個屬性或修改多個現有屬性,可以使用 Object.defineProperties 函數 (JavaScript)。

異常

若是如下任一條件爲 true,則引起 TypeError 異常:

  • object 參數不是對象。
  • 此對象不可擴展且指定的屬性名稱不存在。
  • descriptor 具備 value 或 writable 特性,而且具備 get 或 set 特性。
  • descriptor 具備 get 或 set 特性,上述特性不是函數且已定義。
  • 指定的屬性名稱已存在,現有屬性具備 false 的 configurable 特性,且 descriptor 包含一個或多個與現有屬性中特性不一樣的特性。 可是,當現有屬性具備 false 的 configurable 特性和 true 的 writable 特性時,則容許 value 或 writable 特性不一樣。

添加數據屬性

在如下示例中,Object.defineProperty 函數向用戶定義的對象添加數據屬性。 若改成向現有的 DOM 對象添加屬性,則取消對 var = window.document 行的註釋。

var newLine = "<br />";

// Create a user-defined object.
var obj = {};

// Add a data property to the object.
Object.defineProperty(obj, "newDataProperty", {
    value: 101,
    writable: true,
    enumerable: true,
    configurable: true
});

// Set the property value.
obj.newDataProperty = 102;
document.write("Property value: " + obj.newDataProperty + newLine);

// Output:
// Property value: 102

若要列出對象屬性,請將如下代碼添加到此示例中。

var names = Object.getOwnPropertyNames(obj);
for (var i = 0; i < names.length; i++) {
    var prop = names[i];

    document.write(prop + ': ' + obj[prop]);
    document.write(newLine);
}

// Output:
//  newDataProperty: 102

修改數據屬性

若要修改對象的屬性特性,請將如下代碼添加到前面所示的 addDataProperty 函數。 descriptor 參數只包含 writable 特性。 其餘數據屬性特性保持不變。

// Modify the writable attribute of the property.
Object.defineProperty(obj, "newDataProperty", { writable: false });

// List the property attributes by using a descriptor.
// Get the descriptor with Object.getOwnPropertyDescriptor.
var descriptor = Object.getOwnPropertyDescriptor(obj, "newDataProperty");
for (var prop in descriptor) {
    document.write(prop + ': ' + descriptor[prop]);
    document.write(newLine);
}

// Output
// writable: false
// value: 102
// configurable: true
// enumerable: true

添加訪問器屬性

在如下示例中,Object.defineProperty 函數向用戶定義的對象添加訪問器屬性。

var newLine = "<br />";

// Create a user-defined object.
var obj = {};

// Add an accessor property to the object.
Object.defineProperty(obj, "newAccessorProperty", {
    set: function (x) {
        document.write("in property set accessor" + newLine);
        this.newaccpropvalue = x;
    },
    get: function () {
        document.write("in property get accessor" + newLine);
        return this.newaccpropvalue;
    },
    enumerable: true,
    configurable: true
});

// Set the property value.
obj.newAccessorProperty = 30;
document.write("Property value: " + obj.newAccessorProperty + newLine);

// Output:
// in property set accessor
// in property get accessor
// Property value: 30

若要列出對象屬性,請將如下代碼添加到此示例中。

var names = Object.getOwnPropertyNames(obj);
for (var i = 0; i < names.length; i++) {
    var prop = names[i];

    document.write(prop + ': ' + obj[prop]);
    document.write(newLine);
}
// Output:
// in property get accessor
// newAccessorProperty: 30

修改訪問器屬性

若要修改對象的訪問器屬性,請將如下代碼添加前面所示的代碼。 descriptor 參數只包含 get 訪問器定義。 其餘屬性特性保持不變。

// Modify the get accessor.
Object.defineProperty(obj, "newAccessorProperty", {
    get: function () { return this.newaccpropvalue; }
});

// List the property attributes by using a descriptor.
// Get the descriptor with Object.getOwnPropertyDescriptor.
var descriptor = Object.getOwnPropertyDescriptor(obj, "newAccessorProperty");
for (var prop in descriptor) {
    document.write(prop + ': ' + descriptor[prop]);
    document.write(newLine);
}

// Output:
// get: function () { return this.newaccpropvalue; }
// set: function (x) { document.write("in property set accessor" + newLine); this.newaccpropvalue = x; }
// configurable: true
// enumerable: true

修改 DOM 元素上的屬性

下面的示例演示如何經過使用 Object.getOwnPropertyDescriptor 函數來獲取和修改屬性的屬性描述符,從而自定義內置 DOM 屬性。 對於此示例中,必須經過使用 ID 爲「div」的 DIV 元素。

// Get the querySelector property descriptor.
var descriptor = Object.getOwnPropertyDescriptor(Element.prototype, "querySelector");

// Make the property read-only.
descriptor.value = "query";
descriptor.writable = false;
// Apply the changes to the Element prototype.
Object.defineProperty(Element.prototype, "querySelector", descriptor);

// Get a DOM element from the HTML body.
var elem = document.getElementById("div");

// Attempt to change the value. This causes the revised value attribute to be called.
elem.querySelector = "anotherQuery";
document.write(elem.querySelector);

// Output:
// query

Object.keys()

返回對象的可枚舉屬性和方法的名稱。

語法

Object.keys(object)

參數object:必需。包含屬性和方法的對象。這能夠是您建立的對象或現有文檔對象模型 (DOM) 對象。
返回值:一個數組,其中包含對象的可枚舉屬性和方法的名稱。
異常:若是爲 object 參數提供的值不是對象的名稱,則將引起 TypeError 異常。

備註

keys 方法僅返回可枚舉屬性和方法的名稱。若要返回可枚舉的和不可枚舉的屬性和方法的名稱,可以使用 Object.getOwnPropertyNames 函數 (JavaScript)。
有關屬性的 enumerable 特性的信息,請參見 Object.defineProperty 函數 (JavaScript)和 Object.getOwnPropertyDescriptor 函數 (JavaScript)。

下面的示例建立一個對象,該對象具備三個屬性和一個方法。而後使用 keys 方法獲取該對象的屬性和方法。

js代碼:

// Create a constructor function.
function Pasta(grain, width, shape) {
   this.grain = grain;
   this.width = width;
   this.shape = shape;

   // Define a method.
   this.toString = function () {
       return (this.grain + ", " + this.width + ", " + this.shape);
   }
}

// Create an object.
var spaghetti = new Pasta("wheat", 0.2, "circle");

// Put the enumerable properties and methods of the object in an array.
var arr = Object.keys(spaghetti);
document.write (arr);

// Output:
// grain,width,shape,toString

下面的示例顯示 Pasta 對象中以字母「g」開頭的全部可枚舉屬性的名稱。

// Create a constructor function.
function Pasta(grain, width, shape) {
    this.grain = grain;
    this.width = width;
    this.shape = shape;
}

var polenta = new Pasta("corn", 1, "mush");

var keys = Object.keys(polenta).filter(CheckKey);
document.write(keys);

// Check whether the first character of a string is "g".
function CheckKey(value) {
    var firstChar = value.substr(0, 1);
    if (firstChar.toLowerCase() == "g")
        return true;
    else
        return false;
}

// Output:
// grain

Object.getOwnPropertyNames()

返回對象本身的屬性的名稱。一個對象的本身的屬性是指直接對該對象定義的屬性,而不是從該對象的原型繼承的屬性。對象的屬性包括字段(對象)和函數。

語法

Object.getOwnPropertyNames(object)

參數:object,必需。包含本身的屬性的對象。
返回值:一個數組,其中包含對象本身的屬性的名稱。
異常:若是爲 object 參數提供的值不是對象的名稱,則將引起 TypeError 異常。

備註

getOwnPropertyNames 方法同時返回可枚舉的和不可枚舉的屬性和方法的名稱。若要僅返回可枚舉的屬性和方法的名稱,可以使用 Object.keys 函數 (JavaScript)。

下面的示例建立一個對象,該對象具備三個屬性和一個方法。而後使用 getOwnPropertyNames 方法獲取該對象本身的屬性(包括方法)。

function Pasta(grain, width, shape) {
   // Define properties.
   this.grain = grain;
   this.width = width;
   this.shape = shape;
   this.toString = function () {
       return (this.grain + ", " + this.width + ", " + this.shape);
   }
}

// Create an object.
var spaghetti = new Pasta("wheat", 0.2, "circle");

// Get the own property names.
var arr = Object.getOwnPropertyNames(spaghetti);
document.write (arr);

// Output:
//   grain,width,shape,toString

下面的示例顯示了使用 Pasta 構造函數構造的 spaghetti 對象中以字母「S」開頭的屬性名。

function Pasta(grain, size, shape) {
    this.grain = grain; 
    this.size = size; 
    this.shape = shape; 
}

var spaghetti = new Pasta("wheat", 2, "circle");

var names = Object.getOwnPropertyNames(spaghetti).filter(CheckKey);
document.write(names); 

// Check whether the first character of a string is 's'. 
function CheckKey(value) {
    var firstChar = value.substr(0, 1); 
    if (firstChar.toLowerCase() == 's')
        return true; 
    else
         return false; 
}
// Output:
// size,shape

參考

https://msdn.microsoft.com/zh...
https://msdn.microsoft.com/li...
https://msdn.microsoft.com/zh...
相關閱讀:Javascript apply的巧妙用法以及擴展到Object.defineProperty的使用

相關文章
相關標籤/搜索