fetch API 处理 __proto__ 与分配它的方式不同吗?
Does the fetch API handle __proto__ differently to assigning it?
当我使用 fetch 获取设置了 __proto__
的负载时,它似乎没有像分配给对象那样将其应用于对象原型。
这很好,因为它意味着对象原型永远不会被 API 调用污染,但我有点困惑 why/how 这行得通吗?
例如,如果我有一个 payload.json
:
{
"__proto__": { "toString": "foobar" }
}
并获取这个:
fetch("payload.json")
.then(function(response) {
return response.json();
})
.then(function(json) {
// JSON from a fetch payload
console.log("--JSON from a fetch payload--");
console.log("payload.toString():", json.toString()); // [object Object]
console.log("json.__proto__:", json.__proto__); // {toString: "foobar"}
console.log("{}.toString()", {}.toString()); //[object Object]
//New object with __proto__ set
console.log("--New object with __proto__ set--");
const x = { __proto__: { toString: () => "hacked" } };
console.log("x.toString(): ", x.toString());
console.log("{}.toString(): ", {}.toString());
console.log(
"JSON.parse('{}').toString(): ",
JSON.parse("{}").toString()
);
// Set proto with assignment
console.log("--Set proto with assignment--");
const y = {};
y.__proto__.toString = () => "polluted";
console.log("y.toString(): ", y.toString());
console.log("x.toString(): ", x.toString());
console.log("{}.toString(): ", {}.toString());
console.log(
"JSON.parse('{}').toString(): ",
JSON.parse("{}").toString()
);
});
你得到输出:
--JSON from a fetch payload--
payload.toString(): [object Object]
json.__proto__:
Object {toString: "foobar"}
{}.toString() [object Object]
--New object with __proto__ set--
x.toString(): *** hacked ***
{}.toString(): [object Object]
JSON.parse('{}').toString(): [object Object]
--Set proto with assignment--
y.toString(): *** polluted ***
x.toString(): *** hacked ***
{}.toString(): *** polluted ***
JSON.parse('{}').toString(): *** polluted ***
我理解为什么上一个示例会污染对象原型,但为什么 json.__proto__
在来自提取 api 调用时的处理方式不同?
上面的例子可以在codesandbox.io
上看到
其实这跟fetch
API没有任何关系。
__proto__
是 Object.prototype
上的 getter/setter 属性,它将 [[Get]]
和 [[Set]]
操作转换为 [[GetPrototypeOf]]
和[[SetPrototypeOf]]
,分别
当你 fetch
数据并将其解析为 JSON 时,它的行为与 JSON.parse
相同:解析 JSON,然后创建一个空的 JS 对象,并将属性放在上面。
但是,它们 [[Define]]
属性而不是 [[Set]]
属性,因此它们 不会触发 setter,结果最终有一个名为 __proto__
的常规 属性 指向另一个对象,它的 [[Prototype]]
:
看看下面的例子:
//Using [[Set]], the way you thought of
const obj1 = {}
obj1.__proto__ = {}
console.log(obj1.hasOwnProperty('__proto__')) //false, because it's inherited
console.log(obj1.__proto__ === Object.getPrototypeOf(obj1)) //true
//Using [[Define]], the way how JSON.parse does it
const obj2 = {}
Object.defineProperty(obj2, '__proto__', {
value: {},
enumerable: true,
configurable: true,
writable: true
})
console.log(obj2.hasOwnProperty('__proto__') //true, it is own
console.log(obj.__proto__ === Object.getPrototypeOf(obj)) //false
当我使用 fetch 获取设置了 __proto__
的负载时,它似乎没有像分配给对象那样将其应用于对象原型。
这很好,因为它意味着对象原型永远不会被 API 调用污染,但我有点困惑 why/how 这行得通吗?
例如,如果我有一个 payload.json
:
{
"__proto__": { "toString": "foobar" }
}
并获取这个:
fetch("payload.json")
.then(function(response) {
return response.json();
})
.then(function(json) {
// JSON from a fetch payload
console.log("--JSON from a fetch payload--");
console.log("payload.toString():", json.toString()); // [object Object]
console.log("json.__proto__:", json.__proto__); // {toString: "foobar"}
console.log("{}.toString()", {}.toString()); //[object Object]
//New object with __proto__ set
console.log("--New object with __proto__ set--");
const x = { __proto__: { toString: () => "hacked" } };
console.log("x.toString(): ", x.toString());
console.log("{}.toString(): ", {}.toString());
console.log(
"JSON.parse('{}').toString(): ",
JSON.parse("{}").toString()
);
// Set proto with assignment
console.log("--Set proto with assignment--");
const y = {};
y.__proto__.toString = () => "polluted";
console.log("y.toString(): ", y.toString());
console.log("x.toString(): ", x.toString());
console.log("{}.toString(): ", {}.toString());
console.log(
"JSON.parse('{}').toString(): ",
JSON.parse("{}").toString()
);
});
你得到输出:
--JSON from a fetch payload--
payload.toString(): [object Object]
json.__proto__:
Object {toString: "foobar"}
{}.toString() [object Object]
--New object with __proto__ set--
x.toString(): *** hacked ***
{}.toString(): [object Object]
JSON.parse('{}').toString(): [object Object]
--Set proto with assignment--
y.toString(): *** polluted ***
x.toString(): *** hacked ***
{}.toString(): *** polluted ***
JSON.parse('{}').toString(): *** polluted ***
我理解为什么上一个示例会污染对象原型,但为什么 json.__proto__
在来自提取 api 调用时的处理方式不同?
上面的例子可以在codesandbox.io
上看到其实这跟fetch
API没有任何关系。
__proto__
是 Object.prototype
上的 getter/setter 属性,它将 [[Get]]
和 [[Set]]
操作转换为 [[GetPrototypeOf]]
和[[SetPrototypeOf]]
,分别
当你 fetch
数据并将其解析为 JSON 时,它的行为与 JSON.parse
相同:解析 JSON,然后创建一个空的 JS 对象,并将属性放在上面。
但是,它们 [[Define]]
属性而不是 [[Set]]
属性,因此它们 不会触发 setter,结果最终有一个名为 __proto__
的常规 属性 指向另一个对象,它的 [[Prototype]]
:
看看下面的例子:
//Using [[Set]], the way you thought of
const obj1 = {}
obj1.__proto__ = {}
console.log(obj1.hasOwnProperty('__proto__')) //false, because it's inherited
console.log(obj1.__proto__ === Object.getPrototypeOf(obj1)) //true
//Using [[Define]], the way how JSON.parse does it
const obj2 = {}
Object.defineProperty(obj2, '__proto__', {
value: {},
enumerable: true,
configurable: true,
writable: true
})
console.log(obj2.hasOwnProperty('__proto__') //true, it is own
console.log(obj.__proto__ === Object.getPrototypeOf(obj)) //false