1. 程式人生 > 實用技巧 >Javascript基礎鞏固系列——標準庫Object物件

Javascript基礎鞏固系列——標準庫Object物件

全手打原創,轉載請標明出處:https://www.cnblogs.com/dreamsqin/p/13714848.html, 多謝,=。=~(如果對你有幫助的話請幫我點個贊啦)

重新學習JavaScript是因為當年轉前端有點兒趕鴨子上架的意味,我一直在反思我的知識點總是很零散,不能在腦海中形成一個完整的體系,所以這次想通過再次學習將知識點都串聯起來,結合日常開發的專案,達到溫故而知新的效果。與此同時,總結一下我認為很重要但又被我遺漏的知識點~

Object()方法

可以當作工具方法使用,將任意值轉為物件,如果引數為空(或者為undefinednull),返回一個空物件;如果引數是原始型別的值,會轉為對應的包裝物件;如果引數是一個物件,則直接返回該物件(特殊用法,用於判斷變數是否為物件)。

var obj = Object(1);
obj instanceof Object // true
obj instanceof Number // true

// 判斷變數是否為物件
function isObject(value) {
  return value === Object(value);
}

isObject([]) // true
isObject(true) // false

Object的靜態方法

指部署在Object物件自身的方法。

  • Object.keys()Object.getOwnPropertyNames()
    引數為一個物件,返回一個數組,陣列為該物件自身的(而不是繼承的)所有屬性名,區別是前者只返回可列舉(enumerable: true
    )的屬性,由於 JavaScript 沒有提供計算物件屬性個數的方法,所以可以用這兩個方法代替。
var a = ['Hello', 'World'];
Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]

var obj = {
  p1: 123,
  p2: 456
};
Object.keys(obj).length // 2
Object.getOwnPropertyNames(obj).length // 2
  • Object.getOwnPropertyDescriptor()
    獲取某個屬性的描述物件(屬性描述物件說明見下文),第一個引數是目標物件,第二個引數是一個字串,對應目標物件的某個屬性名,只能用於物件自身的屬性,不能用於繼承的屬性。
var obj = { p: 'a' };

Object.getOwnPropertyDescriptor(obj, 'p')
// Object { value: "a",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }
  • Object.defineProperty()
    通過描述物件(屬性描述物件說明見下文),定義或修改某個屬性,然後返回修改後的物件,引數:屬性所在物件、屬性名字串、屬性描述物件。
var obj = Object.defineProperty({}, 'p', {
  value: 123,
  writable: false,  // 如果原型物件的某個屬性的writable為false,那麼子物件將無法自定義這個屬性,但可以通過defineProperty修改value來繞過限制。
  enumerable: true,
  configurable: false
});

obj.p // 123

obj.p = 246;  // 正常模式下只是默默失敗,嚴格模式(use strict)下會報錯
obj.p // 123
  • Object.defineProperties()
    通過描述物件(屬性描述物件說明見下文),定義或修改多個屬性,然後返回修改後的物件,引數:屬性所在物件、屬性名與屬性描述物件的鍵值對物件。
var obj = Object.defineProperties({}, {
  p1: { value: 123, enumerable: true },
  p2: { value: 'abc', enumerable: true },
  p3: { get: function () { return this.p1 + this.p2 },
    enumerable:true,
    configurable:true
  }
});

obj.p1 // 123
obj.p2 // "abc"
obj.p3 // "123abc"
  • Object.create()
    可以指定原型物件和屬性,返回一個新的物件。
  • Object.getPrototypeOf()
    獲取物件的Prototype物件。

Object的例項方法

指定義在Object.prototype物件上的方法,所有Object的例項物件都繼承了這些方法。

  • Object.prototype.valueOf()
    返回當前物件對應的值,預設情況下返回物件本身,主要用途是JavaScript自動型別轉換時會預設呼叫這個方法。
var obj = new Object();
1 + obj // "1[object Object]"
  • Object.prototype.toString()
    返回當前物件對應的字串形式,預設情況下返回型別字串"[object object]"(第二個值表示該物件的建構函式),陣列、字串、函式、Date 物件都分別部署了自定義的toString方法,覆蓋了原生的Object.prototype.toString方法。
var o = {a:1};
o.toString() // "[object Object]"

// 陣列
[1, 2, 3].toString() // "1,2,3"

// 字串
'123'.toString() // "123"

// 函式
(function () {
  return 123;
}).toString()
// "function () {
//   return 123;
// }"

// Date物件
(new Date()).toString()
// "Tue May 10 2016 09:11:31 GMT+0800 (CST)"

特殊應用:用於判斷資料型別,由於例項物件可能會自定義toString方法,覆蓋掉Object.prototype.toString方法,所以可以利用call直接呼叫原型方法。

Object.prototype.toString.call(value)

//數值:返回[object Number]
//字串:返回[object String]
//布林值:返回[object Boolean]
//undefined:返回[object Undefined]
//null:返回[object Null]
//陣列:返回[object Array]
//arguments 物件:返回[object Arguments]
//函式:返回[object Function]
Object.prototype.toString.call(Math) // "[object Math]"
//Error 物件:返回[object Error]
//Date 物件:返回[object Date]
//RegExp 物件:返回[object RegExp]
//其他物件:返回[object Object]

//一個比typeof運算子更準確的型別判斷函式
var type = function (o){
  var s = Object.prototype.toString.call(o);
  return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};

type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"
  • Object.prototype.toLocaleString()
    返回當前物件對應的本地字串形式,主要作用是留出一個介面,讓各種不同的物件實現自己版本的toLocaleString,用來返回針對某些地域的特定的值,目前ArrayNumberDate自定義了toLocaleString方法。
var person = {
  toString: function () {
    return 'Henry Norman Bethune';
  },
  toLocaleString: function () {
    return '白求恩';
  }
};
person.toString() // Henry Norman Bethune
person.toLocaleString() // 白求恩

var date = new Date();
date.toString() // "Tue Jan 01 2018 12:01:33 GMT+0800 (CST)"
date.toLocaleString() // "1/01/2018, 12:01:33 PM"
  • Object.prototype.hasOwnProperty()
    判斷某個屬性是否為當前物件自身的屬性,還是繼承自原型物件的屬性。
var obj = {
  p: 123
};

obj.hasOwnProperty('p') // true
obj.hasOwnProperty('toString') // false
  • Object.prototype.isPrototypeOf()
    判斷當前物件是否為另一個物件的原型。
  • Object.prototype.propertyIsEnumerable()
    判斷某個屬性是否可列舉,只能用於判斷物件自身的屬性,對於繼承的屬性一律返回false。
var obj = {};
obj.p = 123;

obj.propertyIsEnumerable('p') // true
obj.propertyIsEnumerable('toString') // false

屬性描述物件

JavaScript 提供了一個內部資料結構,用來描述物件的屬性,控制它的行為,比如該屬性是否可寫、可遍歷等等。這個內部資料結構稱為“屬性描述物件”(attributes object)。每個屬性都有自己對應的屬性描述物件,儲存該屬性的一些元資訊。
PS:value+writable:true屬性與get+set不能共存,在Object.defineProperty()Object.defineProperties()引數裡面的屬性描述物件,writableconfigurableenumerable這三個屬性的預設值都為false

 // 屬性描述物件的各個屬性稱為“元屬性”,因為它們可以看作是控制屬性的屬性
{
  value: 123,  // 屬性值,預設為undefined
  writable: false,  // 布林值,表示屬性值(value)是否可改變(即是否可寫),預設為true
  enumerable: true,  // 布林值,表示該屬性是否可遍歷,預設為true(不可遍歷時for...in迴圈、Object.keys()、JSON.stringify會跳過該屬性)
  configurable: false,  // 布林值,表示可配置性,預設為true,控制了屬性描述物件的可寫性(不可配置時無法刪除該屬性,也不得改變該屬性的屬性描述物件(value屬性在writable為true時除外,writable的true改false除外))
  get: undefined,  // 表示該屬性的取值函式(getter),預設為undefined,取值時會呼叫
  set: undefined  // 表示該屬性的存值函式(setter),預設為undefined,存值時會呼叫
}
  • 存取器settergetter
    存值函式稱為setter,使用屬性描述物件的set屬性;取值函式稱為getter,使用屬性描述物件的get屬性。
// 寫法一(enumerable、configurable預設為false)
var obj = Object.defineProperty({}, 'p', {
  get: function () {
    return 'getter';
  },
  set: function (value) {
    console.log('setter: ' + value);
  }
});

obj.p // "getter"
obj.p = 123 // "setter: 123"

// 寫法二(更推薦,因為enumerable、configurable預設為true)
var obj = {
  get p() {
    return 'getter';
  },
  set p(value) {
    console.log('setter: ' + value);
  }
};

物件的拷貝

將一個物件的所有屬性,拷貝到另一個物件,為了能把存取器定義的屬性也成功拷貝,可以使用以下方法。

var extend = function (to, from) {
  for (var property in from) {
    if (!from.hasOwnProperty(property)) continue;  // 過濾掉繼承的屬性
    Object.defineProperty(
      to,
      property,
      Object.getOwnPropertyDescriptor(from, property)
    );
  }
  return to;
}

extend({}, { get a(){ return 1 } })
// { get a(){ return 1 } })

控制物件狀態

有時需要凍結物件的讀寫狀態,防止物件被改變(但有漏洞:可以通過改變原型物件,來為物件增加屬性;如果屬性值是物件,就只能凍結屬性指向的物件(即無法指向其他值),而不能凍結物件本身的內容)。JavaScript 提供了三種凍結方法,最弱的一種是Object.preventExtensions,其次是Object.seal,最強的是Object.freeze

  • Object.preventExtensions()
    可以使得一個物件無法再新增新的屬性。
var obj = new Object();
Object.preventExtensions(obj);

Object.defineProperty(obj, 'p', {
  value: 'hello'
});
// TypeError: Cannot define property:p, object is not extensible.

obj.p = 1;
obj.p // undefined
  • Object.isExtensible()
    用於檢查一個物件是否使用了Object.preventExtensions方法,也就是說,檢查是否可以為一個物件新增屬性。
var obj = new Object();

Object.isExtensible(obj) // true
Object.preventExtensions(obj);
Object.isExtensible(obj) // false
  • Object.seal()
    使得一個物件既無法新增新屬性,也無法刪除舊屬性,實質是把屬性描述物件的configurable屬性設為false
var obj = { p: 'hello' };
Object.seal(obj);

delete obj.p;
obj.p // "hello"

obj.x = 'world';
obj.x // undefined
  • Object.isSealed()
    用於檢查一個物件是否使用了Object.seal方法。
var obj = { p: 'a' };

Object.seal(obj);
Object.isSealed(obj) // true
Object.isExtensible(obj) // false
  • Object.freeze()
    可以使得一個物件無法新增新屬性、無法刪除舊屬性、也無法改變屬性的值,使得這個物件實際上變成了常量。
var obj = {
  p: 'hello'
};

Object.freeze(obj);

obj.p = 'world';
obj.p // "hello"

obj.t = 'hello';
obj.t // undefined

delete obj.p // false
obj.p // "hello"
  • Object.isFrozen()
    用於檢查一個物件是否使用了Object.freeze方法。
var obj = {
  p: 'hello'
};

Object.freeze(obj);
Object.isFrozen(obj) // true
Object.isExtensible(obj) // false

參考資料

JavaScript 語言入門教程 :https://wangdoc.com/javascript/index.html