先核來源與已訂規則
本例原 JSON 有自有 __proto__ 成員。用 Object.assign 填普通空物件,會走目標設值行為,可能改它的原型。更正以 Object.fromEntries 建自有資料屬性。
| 紀錄 | 輸入 JSON |
|---|---|
| 原例輸入 | {"raw":"{\"__proto__\":{\"mark\":\"changed\"},\"label\":\"A\"}"} |
比較兩條實際路徑
錯目標丟自有成員,卻繼承 changed 標記。更正有 __proto__、label 自有鍵,沒有繼承標記;例子只涉及新建本地目標。
| 路徑 | 輸出 JSON |
|---|---|
| 錯誤路徑 | {"ownKeys":["label"],"prototypeMark":"changed","ownData":[["label","A"]]} |
| 更正路徑 | {"ownKeys":["__proto__","label"],"prototypeMark":null,"ownData":[["__proto__",{"mark":"changed"}],["label","A"]]} |
import {DatabaseSync} from 'node:sqlite';
import {TextDecoder} from 'node:util';
import {inflateSync} from 'node:zlib';
import {posix} from 'node:path';
import {createHash} from 'node:crypto';
const rows=(db,sql)=>db.prepare(sql).all().map(r=>({...r}));
const failed=(action)=>{try{action();return null;}catch(e){return {code:e.errcode??e.code??e.name};}};
import assert from 'node:assert/strict';
const run=function run(i,corrected=true){
const parsed=JSON.parse(i.raw);const object=corrected?Object.fromEntries(Object.entries(parsed)):Object.assign({},parsed);
return {ownKeys:Object.keys(object),prototypeMark:Object.getPrototypeOf(object).mark??null,ownData:Object.entries(object)};
};
const cases=[{"label":"misleading base","input":{"raw":"{\"__proto__\":{\"mark\":\"changed\"},\"label\":\"A\"}"},"expected":{"ownKeys":["label"],"prototypeMark":"changed","ownData":[["label","A"]]},"corrected":false},{"label":"corrected base","input":{"raw":"{\"__proto__\":{\"mark\":\"changed\"},\"label\":\"A\"}"},"expected":{"ownKeys":["__proto__","label"],"prototypeMark":null,"ownData":[["__proto__",{"mark":"changed"}],["label","A"]]},"corrected":true},{"label":"constructor remains own data","input":{"raw":"{\"constructor\":\"C\"}"},"expected":{"ownKeys":["constructor"],"prototypeMark":null,"ownData":[["constructor","C"]]},"corrected":true},{"label":"text proto member remains data","input":{"raw":"{\"__proto__\":\"P\"}"},"expected":{"ownKeys":["__proto__"],"prototypeMark":null,"ownData":[["__proto__","P"]]},"corrected":true}];
for(const test of cases){
const actual=run(test.input,test.corrected);
assert.deepEqual(actual,test.expected,test.label);
console.log(test.label,actual);
}
下次載入前核邊界
constructor 及文字值 __proto__,在更正路徑都照作自有資料。
| 情況 | 輸入 JSON | 預期輸出 JSON |
|---|---|---|
| 自有 constructor 成員 | {"raw":"{\"constructor\":\"C\"}"} | {"ownKeys":["constructor"],"prototypeMark":null,"ownData":[["constructor","C"]]} |
| proto 成員為文字值 | {"raw":"{\"__proto__\":\"P\"}"} | {"ownKeys":["__proto__"],"prototypeMark":null,"ownData":[["__proto__","P"]]} |
已收來源的 __proto__ 應否消失?
查看參考答案
不應,此約定要保成自有成員,同時不讓它取代物件原型。