Object()的方法一覽

在JavaScript中,萬物皆來自對象,可是對Object()這個對象的構造函數卻只知其一;不知其二,因此借這個機會來縷清楚Object的方法,並提供低版本的瀏覽器中兼容方法。react

//從chrome瀏覽器中的控制檯輸入Object.getOwnPropertyNames(Object),就可以獲得Object對象全部自身屬性和方法。
Array ["length", "name", "arguments", "caller", "prototype", "assign", "getOwnPropertyDescriptor",
"getOwnPropertyDescriptors", "getOwnPropertyNames", "getOwnPropertySymbols", "is", "preventExtensions", "seal",
"create", "defineProperties", "defineProperty", "freeze", "getPrototypeOf", "setPrototypeOf", "isExtensible",
"isFrozen", "isSealed", "keys", "entries", "values"]
//從輸出臺中咱們能夠知道Object共有25個屬性和方法,固然因爲瀏覽器的兼容性問題,在Firefox瀏覽器中輸入,獲得以下
Array [ "assign", "getPrototypeOf", "setPrototypeOf", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors",
"keys", "values", "entries", "is", "defineProperty","defineProperties","creat","getOwnPropertyNames",
"getOwnPropertySymbols","preventExtensions", "seal","isFrozen", "isSealed","prototype","length","name",
"freeze","isExtensible"]
//因此爲了最大限度的獲得Object中的方法,我將以chrome版本獲得的數據爲基準,對Object的方法進行解析,一些還處於試驗
//狀態也會提到。複製代碼

Object.assign()

Object.assign(target,source) 中將sources對象中全部可枚舉的屬性的值複製到目標的對象中,其會返回目標對象。該方法的兼容性不是很好,IE全面淪陷,在移動端方面,僅有少數的瀏覽器才兼容該方法。幸虧的是在MDN上提供了兼容的方法。git

if(typeof Object.assign!='function'){
  Object.assign = function(target){
    'use strict';
    if(target = null){
      throw new TypeError('Cannot convert undefined or null to object');
    }
    target = Object(target);
    for(var index=0;index
  
  
  

 複製代碼

其實若是咱們仔細觀看源碼的時候就會發現一個事實,那就是Object.assign()只是對一級屬性進行復制,而不會對對象裏面的對象進行深度拷貝,若是出現同名屬性的key值,那麼後者會覆蓋前者,並不能作到完整的融合,若是要進行融合的話,能夠前往depp-assign中閱讀github

Object.create()

Object.create(__proto__,[properties]) 該方法將__proto__做爲原型對象,並將[properties]做爲新對象的屬性。web

Object.defineProperty()

Object.defineProperty(obj,prop,descriptor) 方法在obj對象上對prop屬性進行定義或修改,其中descriptor爲被定義或修改的屬性符。其中對於descriptor屬性符能夠設置的值以下顯示:chrome

  • 【value】表示屬性的值,默認爲undefined
  • 【writable】該屬性是否爲可寫,若是直接在對象上定義屬性,則默認爲true。若是設置爲false,則屬性僅爲可讀。
  • 【configurable】 若是爲false的話,則不能修改(writabel,configurable,enumerable),若是直接在對象上定義屬性,則默認爲true
  • 【enumerable】是否可以被枚舉,若是直接在對象上定義屬性,則默認爲true。
  • 【get】當對象訪問prop屬性的實話,會調用這個方法,並返回結果。默認爲undefined
  • 【set】當對象設置該屬性的時候,會調用這個方法,默認爲undefined。

在MVVM框架中的雙向數據綁定是經過 Object.defineProperty() 來實現的,其核心的代碼以下所示:數組

function defineReactive(obj, key, value) {
    var dep = new Dep()
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter() {
            if (Dep.target) {
                dep.depend()
            }
            return value
        },
        set: function reactiveSetter(newVal) {
            if (value === newVal) {
                return
            } else {
                value = newVal
                dep.notify()
            }
        }
    })
}複製代碼

Object.defineProperty(obj,prop,descriptor) 目前兼容性作的不錯,移動端上兼容絕大部分的瀏覽器,PC上也可以兼容到IE9及以上。注意,該方法在IE8上也是可以使用的,可是其只可以傳入DOM對象,若是傳入其餘對象會報錯。可能有人會對該方法的做用產生疑問,其實該方法極大的優化了對象的獲取和修改屬性方式,如下是來自騰訊AlloyTeam的一個示例:瀏覽器

//當作動畫效果的時候,經常這樣作,很是的繁瑣
var targetDom = document.getElementById('target');
var transformText = 'translateX(' + 10 + 'px)';
targetDom.style.webkitTransform = transformText;
targetDom.style.transform = transformText;
//有了Object.defineProperty()以後就能夠這樣作了
Object.defineProperty(targetDom, 'translateX', {
    set: function(value) {
         var transformText = 'translateX(' + value + 'px)';
        dom.style.webkitTransform = transformText;
        dom.style.transform = transformText;
    }
}
//這樣再後面調用的時候, 十分簡單
dom.translateX = 10;
dom.translateX = -10;複製代碼

可能有人以爲這樣作仍是有點繁瑣,可是咱們能夠封裝一個函數庫專門作動畫效果。
具體的GitHub地址能夠前往這裏觀看Object.defineProperty動畫應用框架

Object.defineProperties()

Object.defineProperties(obj,props) 方法直接在一個對象上修改或建立屬性,並返回修改後的對象,其與上面那個方法的區別在於前者能夠修改或定義多個屬性,可是後者能夠定義或修改多個,同時兩者的兼容性同樣。對於前者而言,有相應的polyfill函數。以下顯示:dom

function defineProperties(obj, properties)
{
  function convertToDescriptor(desc){
    function hasProperty(obj, prop){
      return Object.prototype.hasOwnProperty.call(obj, prop);
    }

    function isCallable(v){
      // 若是除函數之外,還有其餘類型的值也能夠被調用,則能夠修改下面的語句
      return typeof v === "function";
    }

    if (typeof desc !== "object" || desc === null)
      throw new TypeError("不是正規的對象");

    var d = {};
    if (hasProperty(desc, "enumerable"))
      d.enumerable = !!desc.enumerable;
    if (hasProperty(desc, "configurable"))
      d.configurable = !!desc.configurable;
    if (hasProperty(desc, "value"))
      d.value = desc.value;
    if (hasProperty(desc, "writable"))
      d.writable = !!desc.writable;
    if (hasProperty(desc, "get")){
      var g = desc.get;
      if (!isCallable(g) && g !== "undefined")
        throw new TypeError("bad get");
      d.get = g;
    }
    if (hasProperty(desc, "set")){
      var s = desc.set;
      if (!isCallable(s) && s !== "undefined")
        throw new TypeError("bad set");
      d.set = s;
    }

    if (("get" in d || "set" in d) && ("value" in d || "writable" in d))
      throw new TypeError("identity-confused descriptor");

    return d;
  }

  if (typeof obj !== "object" || obj === null)
    throw new TypeError("不是正規的對象");

  properties = Object(properties);
  var keys = Object.keys(properties);
  var descs = [];
  for (var i = 0; i < keys.length; i++)
    descs.push([keys[i], convertToDescriptor(properties[keys[i]])]);
  for (var i = 0; i < descs.length; i++)
    Object.defineProperty(obj, descs[i][0], descs[i][1]);

  return obj;
}複製代碼

Object.getOwnPropertyDescriptor(obj,prop)

該方法是用於若是prop屬性存在對象obj上,則返回其屬性描述符,若是不存在就返回undefined。該屬性描述符由下面的屬性所組成。ide

  • 【value】表示屬性的值,僅針對數據屬性描述符有效
  • 【writable】當且僅當屬性的值能夠被改變時爲true。(僅針對數據屬性描述有效)
  • 【configurable】 當且僅當指定對象的屬性描述能夠被改變或者屬性可被刪除時,爲true。
  • 【enumerable】當且僅當指定對象的屬性能夠被枚舉出時,爲 true。
  • 【get】獲取該屬性的訪問器函數(getter)。若是沒有訪問器, 該值爲undefined。
  • 【set】獲取該屬性的設置器函數(setter)。若是沒有設置器,該值爲undefined。(僅針對包含訪問器或設置器的屬性描述有效)
    該方法可以在IE8及以上的瀏覽器上運行。

Object.getOwnPropertyNames(obj)

該方法返回obj上全部自身可枚舉和不可枚舉的屬性(不包括原型鏈上的屬性),若是傳入的obj不是數組,則會報錯。該方法的兼容IE9及以上的瀏覽器。

Object.getPrototypeOf(obj)

該方法返回對象的原型對象,若是沒有的話,則返回null。須要指出的是,對於函數對象,其返回的並非顯式原型(prototype),而是隱式原型(__proto__),該方法兼容IE9及以上的瀏覽器。

Object.is(val1,val2)

該方法是肯定兩個值是不是相同的值,這個方法與===相比,其會將-0和+0當作不等,而且對於兩個NaN的比較,Object.is()會當作是相等的,而===會將0、-0、+0當作相等的,兩個NaN當作不等。如下是Object.is()的示例:

Object.is(0,-0)//false
Object.is(-0,-0);//true
Object.is(NaN,0/0); //true
Object.is(5,5/1); //true複製代碼

該方法在微軟公司出的瀏覽器上只支持EDGE瀏覽器。不過,萬幸的是MDN提供了相應的解決方案。

if (!Object.is) {
  Object.is = function(x, y) {
    if (x === y) { 
      return x !== 0 || 1 / x === 1 / y;
    } else {
      return x !== x && y !== y;
    }
  };
}複製代碼

Object.preventExtensions()

該方法可讓一個對象永遠不能添加新的屬性,在嚴格模式下,若是強行爲對象添加屬性,會報錯,如下是Object.isExtensible()的注意事項:

"use strict";
var obj = {name:"zhang"};
obj.name = "li"//能夠進行修改
Object.preventExtensions(obj);
//obj.age = 14;嚴格模式下會報錯
obj.__proto__.age = 13;
console.log(obj);//可以在原型對象上添加屬性
obj.__proto__ = {}//不能直接重定義原型,會報錯。複製代碼

Object.seal(obj)

其對一個對象進行密封,並返回被密封的對象,這些對象都是不可以添加屬性,不能刪除已有屬性,以及不可以修改已有屬性的可枚舉型、可配置型、可寫性。

Object.freeze(obj)

該方法將obj對象凍結,其任何屬性都是不能夠被修改的。如今咱們演示下這個用法。

var obj = {name:"zhangsan",prop:{age:23,sex:"man"}};
Object.freeze(obj);
obj.name = "lisi";
console.log(obj.name);//"zhangsan
//咱們使用Object.defineProperty()方法來修改屬性
Object.defineProperty(obj,'prop',{"age":32,sex:"female"});
console.log(obj.prop);
//{age: 23, sex: "man"}貌似仍是不行,咱們換種方式看看
Object.prop.age = 25;
console.log(Object.prop);
//{age: 25, sex: "man"}
//這個對象竟然改變了,明明已經凍結了,爲何起屬性仍是能夠發生變化複製代碼

這就要說到Object.freeze(obj) 的特性了,其只是一個淺凍結。何爲淺凍結?淺凍結僅僅是對對象的一級屬性進行凍結,像上面代碼中所演示的那樣,若是直接修改其name和prop屬性是不能被修改的。若是屬性也是一個對象的話,那將不同了,直接對屬性中的屬性就行修改,如Object.prop.age = 25;同樣,是能夠修改的。既然有淺凍結,就必定有深凍結了,那怎麼才能實現深凍結呢?

//咱們能夠配合遞歸實現
Object.prototype.deepFreeze = Object.prototype.deepFreeze || function (o){
    var prop, propKey;
    Object.freeze(o); // 首先凍結第一層對象
    for (propKey in o){
        prop = o[propKey];
        if(!o.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)){
            continue;
        }
        deepFreeze(prop); // 遞歸
    }
}複製代碼

能夠有人會對preventExtensions,seal,freeze這三個方法產生疑問,這三個方法對從擴展、密封和凍結三個方面對對象進行讀寫狀態的控制,防止對象被改變。其中最弱的一層是preventExtensions,只能讓對象沒法添加新的屬性,其次是seal,該方法沒法添加屬性,也沒法刪除屬性,最後是freeze。固然,上面這三種方法仍是能夠經過改變對象的原型對象,來增長原型鏈上的屬性,而且都使淺凍結。有對對象進行控制的方法,就確定有判斷其是否被控制的方法,Object.isExtensible()Object.isSealed()Object.isfreeze(obj)。這三個是判斷是否被控制,在此就再也不贅述。

Object.keys(obj)

該方法會返回obj上全部能夠進行枚舉的屬性的字符串數組,以下所示:

//數組對象
var arr  =[3,4,5];
console.log(Object.keys(obj))
//[0,1,2]
var obj = {}
console.log(Object.keys(obj))
//[],其不會遍歷原型鏈上的屬性。複製代碼

該方法兼容IE9及以上的瀏覽器,可是有相應的解決方法。

if (!Object.keys) {
  Object.keys = (function () {
    var hasOwnProperty = Object.prototype.hasOwnProperty,
        hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
        dontEnums = [
          'toString',
          'toLocaleString',
          'valueOf',
          'hasOwnProperty',
          'isPrototypeOf',
          'propertyIsEnumerable',
          'constructor'
        ],
        dontEnumsLength = dontEnums.length;
    return function (obj) {
      if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
      var result = [];
      for (var prop in obj) {
        if (hasOwnProperty.call(obj, prop)) result.push(prop);
      }
      if (hasDontEnumBug) {
        for (var i=0; i < dontEnumsLength; i++) {
          if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
        }
      }
      return result;
    }
  })()
};複製代碼

getOwnPropertySymbols(obj)

該方法返回obj對象上自身的(非繼承的)全部Symbol屬性鍵。

Object.entries()、Object.getOwnPropertyDescriptors()、Object.values()這些方法還只是處於試驗階段的,因此不在這裏進行展開。

相關文章
相關標籤/搜索