json雖然簡單,但這些細節你未必知道
基本介紹
JSON的全稱是JavaScript Object Notation,它並不是程式語言,而是一種可以在伺服器和客戶端之間傳輸的資料格式,本來是JavaScript的子集,但現在已獨立存在於各種程式語言中。
它有以下使用場景
- 網路資料傳遞時,比如http請求中引數
- 專案裡某些配置檔案,比如package.json檔案
- 非關係型資料庫(NoSQL)將json作為儲存格式
語法
它的檔案以 .json 為字尾名,但json檔案頂層的程式碼有嚴格限制,只能寫以下三種,不然程式碼會直接標紅~
1、簡單值
數字(Number)、字串(String,不支援單引號)、布林型別(Boolean)、null型別
2、物件值
由key、value組成,key是字串型別,必須新增雙引號,值可以是簡單值、物件值、陣列值
3、陣列值
簡單值、物件值、陣列值
序列化 stringify
在http請求中攜帶引數經常用到json格式,但我們一般不會在程式碼中直接使用json,因為json資料中操作屬性並不方便,大多數時候是使用物件,將物件轉成json格式就可以通過 stringify 方法。
stringify方法有三個引數
- 引數一(必傳),傳入一個物件,表示對於哪個物件進行stringify操作
- 引數二(可選),傳入陣列或者函式,數組裡包括物件的key值,表示對於物件中的指定key值的資料進行序列化,傳入函式表示對指定的key/value值進行操作
- 引數三(可選),用於改變序列化之後的json資料展現格式
我們對以下物件進行操作
const user = {
name: "alice",
age: 20,
friends: ["lisa", "macus", "windy"],
info: {
teacher: "kiki",
},
};
直接轉換
當只傳入一個引數時,進行基本的序列化操作
const str1 = JSON.stringify(user);
console.log(str1);
操作指定的key值
const str2 = JSON.stringify(user, ["name", "friends"]); const str3 = JSON.stringify(user, (key, value) => { if (key === "age") { return value + 1; } return value; }); console.log(str2); console.log(str3);
當傳入第二個引數時,傳入陣列,表示只對 key值為“name”,“friends”的資料進行序列化;傳入函式,表示操作 key 值為“age”的時候,value+1
改變json展現格式
const str4 = JSON.stringify(user, null, 2);
const str5 = JSON.stringify(user, null, "*");
console.log(str4);
console.log(str5);
傳入第三個引數,2表示換行空2格,* 表示換行及每行內容前加 * 號
toJson方法
如果原物件中有toJSON方法,那麼stringify方法直接呼叫toJSON方法。我們給上面的物件加上toJSON方法,所有的stringify方法的執行結果都會變化。
const user = {
name: "alice",
age: 20,
friends: ["lisa", "macus", "windy"],
info: {
teacher: "kiki",
},
toJSON(){
return 'hello world'
}
};
const str1 = JSON.stringify(user);
const str2 = JSON.stringify(user, ["name", "friends"]);
const str3 = JSON.stringify(user, (key, value) => {
if (key === "age") {
return value + 1;
}
return value;
});
const str4 = JSON.stringify(user, null, 2);
const str5 = JSON.stringify(user, null, "*");
console.log(str1);
console.log(str2);
console.log(str3);
console.log(str4);
console.log(str5);
stringify方法的執行結果都變成了 toJSON 方法的返回值
解析 parse
介面請求返回的引數中一般是json資料,我們要使用首先得通過parse方法將它轉成物件。
parse方法可以接收兩個引數
- 引數一(必傳),json資料,表示將哪一個json資料轉成物件
- 引數二(可選),傳入函式,表示對指定的key/value值進行操作
const str =
'{"name":"alice","age":21,"friends":["lisa","macus","windy"],"info":{"teacher":"kiki"}}';
const obj1 = JSON.parse(str)
const obj2 = JSON.parse(str, (key, value)=>{
if(key === 'age'){
return value - 1
}
return value
})
console.log(obj1)
console.log(obj2)
傳入函式,處理 key值為age時的資料,此時操作 value - 1
拷貝
拷貝有以下幾種形式,拷貝出來的記憶體地址指向不一樣
直接賦值
通過等於符號可以將一個物件賦值給另一個物件。
const user = {
name: "alice",
info: {
hobbies: "tennis",
},
};
const person = user;
user.name = "lisa";
console.log("user", user);
console.log("person", person);
但它們其實指向的是同一個物件,如果操作其中一個物件的值,另外一個物件也會發生變化
在記憶體中表現如下
淺拷貝
淺拷貝只會遍歷一層,如果物件中還有value值為物件或者陣列的情況,那麼更深一層不會被拷貝,展開運算子或者Object.assign可以進行淺拷貝。
const user = {
name: "alice",
info: {
hobbies: "tennis",
},
};
const consumer = { ...user };
user.name = "lisa";
user.info.hobbies = "swimming";
console.log("user", user);
console.log("consumer", consumer);
淺拷貝後,user和consumer已經不是同一個物件了,但他們倆當中的info仍然指向同一個物件,修改其中一個info中的屬性,另一個也會變化
在記憶體中表現如下
深拷貝
深拷貝表示拷貝出來的物件與原物件完全無關,操作任意屬性都不會互相影響,通過 stringify 和 parse 方法可以實現深拷貝。
const user = {
name: "alice",
info: {
hobbies: "tennis",
},
};
const human = JSON.parse(JSON.stringify(user));
user.name = "lisa";
user.info.hobbies = "swimming";
console.log("user", user);
console.log("human", human);
此時user和human不是指向同一個物件,他們中的info物件也不是同一個物件
在記憶體中表現如下
stringify和parse實現深拷貝存在問題
雖然stringify和parse可以實現深拷貝,但是這種方式仍存在一些問題,如果物件中存在【方法、undefined、Symbol】,會直接被移除
const user = {
name: "alice",
height: undefined,
[Symbol("age")]: 20,
info: {
hobbies: "tennis",
},
study() {
console.log("I love reading~");
},
};
const people = JSON.parse(JSON.stringify(user));
console.log("user", user);
console.log("person", people);
只剩下符合json規範的資料
因為存在這種問題,所以一般不會用stringify和parse方法,可以自己編寫處理深拷貝的方法,至於自定義深拷貝方法,留在後面的文章中詳細介紹。
以上就是json相關內容,關於js高階,還有很多需要開發者掌握的地方,可以看看我寫的其他博文,持續更新中~