js學習日記-物件字面量

風靈使發表於2019-04-20

一、物件字面量語法

var person={
    name:'小王',
    age:18,
    _pri:233
} 
  • 成員名稱的單引號不是必須的
  • 最後一個成員結尾不要用逗號,不然在某些瀏覽器中會丟擲錯誤
  • 成員名相同會發生什麼?

es5普通模式下後定義的會覆蓋前面定義的,嚴格模式則會報錯

es6則不管什麼模式都採用後面的覆蓋前面的

  • 成員名可以是動態變數嗎?

es5只能在物件字面量表示式申明以後再新增

var dynamicVar="dyna";
var person={
}
person[dynamicVar]='123';
console.log(person[dynamicVar])

es6則更符合使用場景,可在表示式內建立動態的成員名

var dynamicVar="dyna";
var person={
  [dynamicVar]:'test'
}
console.log(person[dynamicVar])

es6中如果想使用表示式外面的變數名作為成員名,變數的值作為成員值,可進一步簡寫為

var dynamicVar="dyna";
var person={
  dynamicVar, //這是一個語法糖,js引擎會解釋為dynamicVar:'dyna'
  age:15
}
console.log(person.dynamicVar)

注意:此時不能採用person[dynamicVar]方式訪問,因為這句話js引擎會解釋為person['dyna'],物件中沒有dyna屬性,肯定就是undefined

可以採用new person()的方式使用嗎?
肯定是不可以,new關鍵字後面必須是一個建構函式才行,物件字面量哪來的建構函式

二、物件成員配置

物件申明後,會預設為內部的每個成員(屬性或方法)生成一些隱藏屬性,這些隱藏屬性是可以讀取和可配置的:

Object.getOwnPropertyDescriptor()getOwnPropertyDescriptor()-讀取成員的隱藏屬性

Object.definePropertypeObject.defineProperties----設定成員的隱藏屬性

相應的隱藏資訊如下:

configurable-是否可以刪除某個成員,預設為true,需要注意的是,如果該屬性如果定義為false,後續又定義為true的話會報錯

Object.defineProperty(person,'name',{
  configurable:false
})
Object.defineProperty(person,'name',{
 configurable:true
})

writable-成員是否可寫,預設為true

Object.defineProperty(person,'name',{
  writable:false
})
person.name='小李'; //屬性不可寫,嚴格模式下會報錯
console.log(person.name); //輸出小王,說明上面一句無效

enumerable-成員是否可被列舉,預設為true,該屬性主要是用來防範Object.keys()for in的,也就是說該屬性設定對於Object.getOwnPropertyNames()方法是無效的。

使用相應的列舉方法,輸出的結果是一個陣列,那麼陣列中元素的順序是按什麼規則組織的呢?

在es5中並沒有明確這一點,各個瀏覽器有自己的實現,es6中採用Object.keys()for in方法時還是沒有明確,但採用Object.getOwnPropertyNames()方法列舉時,有了相應的標準:

最先放入陣列中的是數值型的成員名,按升序排列;

其次是其它型別的,按新增的先後順序排列

var obj={
  3:'我是1',
  1:'我是1',
  b:'我是b',
  a:'我是a'
}
var names=Object.getOwnPropertyNames(obj);
console.log(names) //["1","3","b","a"]

getset-讀寫成員時呼叫的函式,預設為undefined

在最開始處的物件定義中,我們建立了一個_pri成員,表示這個成員應在內部讀取,下劃線只是一個標記符,並不能限制該成員只能在物件內部訪問。接下來我們來封裝一個屬性讀寫器對_pri成員進行讀取,讀寫器名稱隨意取,這裡叫pri只是為了可讀性

Object.defineProperty(person,'pri',{
  get:function(){
    //做一些其它操作
    console.log('準備獲取_pri的值')
    return _pri;
  },
  set:function(newValue){
    _pri =newValue
  }
})
person.pri='456';
console.log(person.pri);

如果只有get則表示屬性值是隻讀的,只有set表示只能寫。
屬性讀寫器最常用的場景就是在讀取或寫入屬性前可以附帶的做一些操作,達到更好的封裝性

三、物件保護

  • 我不想讓別人在我的物件上新增成員該怎麼辦?

Object.preventExtensions(),該方法用於阻止向物件新增成員,使用Object.isExtensible()判斷物件是否可新增成員

Object.preventExtensions(person);
//新增成員無效,非嚴格模式下什麼都不會發生,嚴格模式下會報錯
person.bankAccount='中國農業銀行'
//依然可以刪除成員,證明了preventExtensions方法只能阻止新增方法
delete person.age;
console.log(person.age) //undefined,表明刪除成功了

我不想讓別人新增、刪除成員
Object.seal()用來阻止新增或刪除成員,判斷物件是否是密封的可採用Object.isSealed()

我不想讓別人新增、刪除成員,也不想讓別人對裡面的成員進行賦值操作
使用Object.freeze()方法後,除了不能新增刪除成員,連成員的賦值都會失效,但是寫入屬性(上面set定義的)依然是有效的
在這裡插入圖片描述

四、其它技巧

  • 實現繼承

Object.create(person)可產生一個具有繼承特性的新物件,但是需要注意的是,父物件的引用型別成員是和子物件共享的,當子物件修改引用型別的成員時,父物件的該成員也會同步發生變化

var person={
  name:'小王',
  age:18,
  _pri:233,
  gf:['豆得兒','張G','TFb']
}
var child=Object.create(person);
child.gf.splice(0,1); //跟豆得兒分手了
console.log(person.gf.length) //父類的gf也變成2了,父子共享女友,尼瑪,太亂了

es6中的Object.setPrototypeOf(obj, prototype)方法可將已有的物件變成繼承關係,其內部原理也跟Object.create一樣,都是將子物件的prototype指向父物件,該方法實現的繼承依然有父子物件共享了引用型別成員的問題

var person={
   age:15
 }
 var man={
     
 }
Object.setPrototypeOf(man,person)
console.log(Object.getPrototypeOf(man)===person) //true
console.log(man.age); //15

如何重寫父物件的成員?-直接在子物件中定義一個同名的成員即可
如何實現在子物件中訪問父物件的成員?-使用super關鍵字

super關鍵字是es6新增的,它是一個指標,指向當前物件的原型,也就是父物件

var person={
   age:15,
   testMethod(){
     console.log('我是父類方法')
   }
 }
 var man={
    //重寫父類方法
    testMethod(){
      console.log('我是子類方法')
      super.testMethod();
    }
 }
Object.setPrototypeOf(man,person)
man.testMethod();

需要注意的是,如果兩個物件不是繼承關係,使用super關鍵字會報錯

  • 一句話實現jquery.extend

jquery.extend是一個典行的物件混入,所謂物件混入,就是將n個物件(為了便於表述,直接叫做輸入物件)組合成一個新物件,新物件具有各個輸入物件的特徵,這在軟體設計模式中叫做裝飾器模式,在es6以前需要自己實現,核心程式碼如下:

function mixins(target,sourceArr){
  sourceArr.forEach(function(source){
     Object.keys(source).forEach(function(item){
       target[item] = source[item]
     })
  })
  return target
}
var obj1={
  name:'123'
}
var obj2={
  id:100
}
var obj3={
  meth(){
    console.log('haha')
  }
}
var target=mixins(obj1,[obj2,obj3])
target.meth()

上面的程式碼實現了一個簡易版的jquery.extend的淺拷貝模式(也就是deep引數為false時的功能),如果多個物件成員同名,則後面的會覆蓋前面的,該程式碼如果要在正式環境使用,還需要加不少的判斷程式碼,但是在es6中一句話就可以實現mixins()函式的功能。

var target=Object.assign(obj1,obj2,obj3)

需要注意的一點就是輸入物件中使用了get修飾符,這時後會有一個轉換,方法名變成了新物件的屬性名,其值為get修飾符方法中的返回值

var obj1={
      name:'123'
    }
    var obj2={
      id:100
    }
    var obj3={
      get getMethod(){
        return '123'
      },
      meth(){
        console.log('haha')
      }
    }
    var target=Object.assign(obj1,obj2,obj3)
    console.log(target.getMethod) //target.getMethod()會報錯,原因是發生了轉換  

相關文章