Skip to content

Commit addcdbf

Browse files
committed
Warn about __proto__.
1 parent afa75eb commit addcdbf

File tree

4 files changed

+74
-2
lines changed

4 files changed

+74
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## Unreleased
2+
- Adds info and warnings about JavaScript engines (e.g. V8) that have the non-standard (as of ECMAScript 5) `__proto__` property.
3+
14
## 1.15.1 (Jul 21, 2016)
25
- Fixes [`Oolong.create`][create] to not mutate the prototype argument under global strict mode when given objects to assign to it.
36
This affected only people running their JavaScript engine (like V8) under global strict mode (`--use-strict`), which seems very rare.

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,47 @@ For extended documentation on all functions, please see the
7878
- [.values](https://github.com/moll/js-oolong/blob/master/doc/API.md#Oolong.values)(object)
7979
- [.wrap](https://github.com/moll/js-oolong/blob/master/doc/API.md#Oolong.wrap)(value, key)
8080

81+
### <a id="__proto__"></a> Warning About `__proto__`
82+
Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto). Assigning to the `__proto__` property, even if done dynamically via `obj[key] = {foo: 42}` changes the object's _prototype_, rather than merely giving it a new property named `__proto__`. That also means if you've got an object with a plain `__proto__` property (like when parsing JSON) and pass it to Oolong's `assign`, it could inadvertently overwrite the target's prototype:
83+
84+
```javascript
85+
var O = require("oolong")
86+
87+
function Person(name) {
88+
this.name = name
89+
}
90+
91+
Person.prototype.greet = function() { return "Hi, " + this.name }
92+
93+
var john = new Person("John")
94+
O.assign(john, JSON.parse("{\"__proto__\": {\"age\": 42}}"))
95+
john.name // => "John"
96+
john.age // => 42
97+
john.greet // => undefined
98+
```
99+
100+
In other situations, like when you're merging two objects recursively with `merge`, this could cause the global prototype (`Object.prototype`) to be modified.
101+
102+
As Oolong.js is written primarily for ECMAScript 5 compliant runtimes with no engine-specific workarounds, it doesn't have special handling for ignoring `__proto__`. Unfortunately, even if it did, the presence of such special properties is far too likely to cause issues elsewhere to make a difference. It's quite common to assign dynamic values to object keys, e.g. when indexing an array (by creating an object with keys as values). Fortunately, you can and should **disable `__proto__` globally** by overwriting it on the global `Object.prototype`:
103+
104+
```javascript
105+
Object.defineProperty(Object.prototype, "__proto__", {
106+
value: undefined, configurable: true, writable: true
107+
})
108+
```
109+
110+
After overwriting `__proto__` on `Object.prototype`, assigning, merging or cloning objects with `__proto__` properties won't behave in any special manner. Assignments to `__proto__` will become regular property assignments:
111+
112+
```javascript
113+
var john = new Person("John")
114+
O.assign(john, JSON.parse("{\"__proto__\": {\"age\": 42}}"))
115+
116+
john.name // => "John"
117+
john.age // => undefined
118+
john.greet() // => "Hi, John"
119+
john.__proto__ // => {age: 42}
120+
```
121+
81122

82123
License
83124
-------

doc/API.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ Returns `target`.
5252

5353
Think of it as _extending_ the first object step by step with others.
5454

55+
**Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
56+
5557
**Examples**:
5658
```javascript
5759
Oolong.assign({name: "John"}, {age: 32}, {shirt: "blue"})
@@ -65,6 +67,8 @@ Returns `target`.
6567

6668
Think of it as _extending_ the first object step by step with others.
6769

70+
**Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
71+
6872
**Examples**:
6973
```javascript
7074
Oolong.assignOwn({name: "John"}, {age: 32}, Object.create({shirt: "blue"}))
@@ -76,6 +80,8 @@ Creates a shallow clone of the given object, taking all enumerable
7680
properties into account.
7781
Shallow means if you've got nested objects, those will be shared.
7882

83+
**Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
84+
7985
**Examples**:
8086
```javascript
8187
Oolong.clone({name: "John", age: 32})
@@ -86,6 +92,8 @@ Oolong.clone({name: "John", age: 32})
8692
Creates a deep clone of the given object, taking all enumerable properties
8793
into account.
8894

95+
**Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
96+
8997
**Examples**:
9098
```javascript
9199
Oolong.cloneDeep({name: "John", attributes: {age: 42}})
@@ -99,6 +107,8 @@ Uses `Object.create` and [`Oolong.assign`](#Oolong.assign)
99107
internally.
100108
Does not modify the given `prototype` nor source objects.
101109

110+
**Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
111+
102112
**Examples**:
103113
```javascript
104114
var PERSON = {name: "Unknown", age: 0}
@@ -118,6 +128,8 @@ will be skipped. Usually that's not a problem, but if you want to use
118128
`Oolong.defaults` for hashmaps/dictionaries with unknown keys, ensure
119129
`target` inherits from `null` instead (use `Object.create(null)`).
120130

131+
**Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
132+
121133
**Examples**:
122134
```javascript
123135
var PERSON = {name: "Unknown", age: 0, shirt: "blue"}
@@ -380,6 +392,8 @@ a plain object. Does not modify anything in the source objects.
380392

381393
Think of it as _extending_ the first object step by step with others.
382394

395+
**Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
396+
383397
**Examples**:
384398
```javascript
385399
var person = {name: "John", attributes: {age: 42}}
@@ -487,7 +501,7 @@ Set the prototype of the given object to the given prototype.
487501
Pass `null` or another object for the prototype.
488502
Returns `object`.
489503

490-
Uses `Object.setPrototypeOf` if it exists. Otherwise uses a polyfill.
504+
Uses `Object.setPrototypeOf` if it exists. Otherwise uses a polyfill that depends on the presence of the non-standard [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) property.
491505

492506
**Examples**:
493507
```javascript

index.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ var SET_PROTO_OF_NULL = "Oolong.setPrototypeOf called on null or undefined"
1818
*
1919
* Think of it as _extending_ the first object step by step with others.
2020
*
21+
* **Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
22+
*
2123
* @example
2224
* Oolong.assign({name: "John"}, {age: 32}, {shirt: "blue"})
2325
* // => {name: "John", age: 32, shirt: "blue"}
@@ -43,6 +45,8 @@ exports.assign = function assign(target) {
4345
*
4446
* Think of it as _extending_ the first object step by step with others.
4547
*
48+
* **Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
49+
*
4650
* @example
4751
* Oolong.assignOwn({name: "John"}, {age: 32}, Object.create({shirt: "blue"}))
4852
* // => {name: "John", age: 32}
@@ -66,6 +70,8 @@ exports.assignOwn = function assignOwn(target) {
6670
* properties into account.
6771
* Shallow means if you've got nested objects, those will be shared.
6872
*
73+
* **Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
74+
*
6975
* @example
7076
* Oolong.clone({name: "John", age: 32})
7177
* // => {name: "John", age: 32}
@@ -82,6 +88,8 @@ exports.clone = function clone(obj) {
8288
* Creates a deep clone of the given object, taking all enumerable properties
8389
* into account.
8490
*
91+
* **Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
92+
*
8593
* @example
8694
* Oolong.cloneDeep({name: "John", attributes: {age: 42}})
8795
* // => {name: "John", attributes: {age: 42}}
@@ -101,6 +109,8 @@ exports.cloneDeep = function cloneDeep(obj) {
101109
* internally.
102110
* Does not modify the given `prototype` nor source objects.
103111
*
112+
* **Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
113+
*
104114
* @example
105115
* var PERSON = {name: "Unknown", age: 0}
106116
* Oolong.create(PERSON, {name: "John"}, {shirt: "blue"})
@@ -128,6 +138,8 @@ exports.create = function create(obj) {
128138
* `Oolong.defaults` for hashmaps/dictionaries with unknown keys, ensure
129139
* `target` inherits from `null` instead (use `Object.create(null)`).
130140
*
141+
* **Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
142+
*
131143
* @example
132144
* var PERSON = {name: "Unknown", age: 0, shirt: "blue"}
133145
* Oolong.defaults({name: "John", age: 42}, PERSON)
@@ -573,6 +585,8 @@ exports.mapKeys = function mapKeys(obj, fn, context) {
573585
*
574586
* Think of it as _extending_ the first object step by step with others.
575587
*
588+
* **Warning**: Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) that could cause unwanted behavior. Please see the [README](https://github.com/moll/js-oolong/blob/master/README.md#__proto__) for more details.
589+
*
576590
* @example
577591
* var person = {name: "John", attributes: {age: 42}}
578592
* Oolong.merge(person, {attributes: {height: 190}})
@@ -777,7 +791,7 @@ exports.reject = function reject(obj, fn, context) {
777791
* Pass `null` or another object for the prototype.
778792
* Returns `object`.
779793
*
780-
* Uses `Object.setPrototypeOf` if it exists. Otherwise uses a polyfill.
794+
* Uses `Object.setPrototypeOf` if it exists. Otherwise uses a polyfill that depends on the presence of the non-standard [`__proto__`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) property.
781795
*
782796
* @example
783797
* var person = {name: "Unnamed", age: 42}

0 commit comments

Comments
 (0)