diff --git a/docs/docs/08-working-with-the-browser.md b/docs/docs/08-working-with-the-browser.md index be4500b3d94..aebc8714bef 100644 --- a/docs/docs/08-working-with-the-browser.md +++ b/docs/docs/08-working-with-the-browser.md @@ -123,11 +123,6 @@ In addition to that philosophy, we've also taken the stance that we, as authors * `String.prototype.split` * `String.prototype.trim` -`es5-sham.js`, also from [kriskowal's es5-shim](https://github.com/es-shims/es5-shim), provides the following that React needs: - -* `Object.create` -* `Object.freeze` - The unminified build of React needs the following from [paulmillr's console-polyfill](https://github.com/paulmillr/console-polyfill). * `console.*` diff --git a/src/isomorphic/classic/element/ReactElement.js b/src/isomorphic/classic/element/ReactElement.js index 9e333270e2f..ab0269673ca 100644 --- a/src/isomorphic/classic/element/ReactElement.js +++ b/src/isomorphic/classic/element/ReactElement.js @@ -14,6 +14,7 @@ var ReactCurrentOwner = require('ReactCurrentOwner'); var assign = require('Object.assign'); +var freeze = require('Object.freeze'); var RESERVED_PROPS = { key: true, @@ -62,8 +63,8 @@ var ReactElement = function(type, key, ref, owner, props) { this._store.validated = false; } this.props = props; - Object.freeze(this.props); - Object.freeze(this); + freeze(this.props); + freeze(this); } }; diff --git a/src/renderers/dom/ReactDOMClient.js b/src/renderers/dom/ReactDOMClient.js index 03609f1e5e6..2d4796a1476 100644 --- a/src/renderers/dom/ReactDOMClient.js +++ b/src/renderers/dom/ReactDOMClient.js @@ -89,16 +89,12 @@ if (__DEV__) { Object.keys, String.prototype.split, String.prototype.trim, - - // shams - Object.create, - Object.freeze, ]; for (var i = 0; i < expectedFeatures.length; i++) { if (!expectedFeatures[i]) { console.error( - 'One or more ES5 shim/shams expected by React are not available: ' + + 'One or more ES5 shims expected by React are not available: ' + 'https://fb.me/react-warning-polyfills' ); break; diff --git a/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js b/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js index 088326ccf8e..c3132764e7e 100644 --- a/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js +++ b/src/renderers/dom/client/syntheticEvents/SyntheticEvent.js @@ -15,6 +15,7 @@ var PooledClass = require('PooledClass'); var assign = require('Object.assign'); +var create = require('Object.create'); var emptyFunction = require('emptyFunction'); var getEventTarget = require('getEventTarget'); @@ -148,7 +149,7 @@ SyntheticEvent.Interface = EventInterface; SyntheticEvent.augmentClass = function(Class, Interface) { var Super = this; - var prototype = Object.create(Super.prototype); + var prototype = create(Super.prototype); assign(prototype, Class.prototype); Class.prototype = prototype; Class.prototype.constructor = Class; diff --git a/src/shared/stubs/Object.create.js b/src/shared/stubs/Object.create.js new file mode 100644 index 00000000000..0c070ab142a --- /dev/null +++ b/src/shared/stubs/Object.create.js @@ -0,0 +1,45 @@ +/** + * Copyright 2014-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule Object.create + */ + +// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.create + +'use strict'; + +var nativeCreate = typeof Object.create === 'function' && Object.create; + +var Type = function() {}; + +function create(prototype, properties) { + if (prototype == null) { + throw new TypeError('This create() implementation cannot create empty objects with create(null)'); + } + + if (typeof prototype !== 'object' && typeof prototype !== 'function') { + throw new TypeError('Object prototype may only be an Object'); + } + + if (properties) { + throw new TypeError('This create() implementation does not support assigning properties'); + } + + var object; + if ( nativeCreate ) { + object = nativeCreate(prototype); + } else { + Type.prototype = prototype; + object = new Type(); + Type.prototype = null; + } + + return object; +} + +module.exports = create; diff --git a/src/shared/stubs/Object.freeze.js b/src/shared/stubs/Object.freeze.js new file mode 100644 index 00000000000..56ac3b3bb84 --- /dev/null +++ b/src/shared/stubs/Object.freeze.js @@ -0,0 +1,37 @@ +/** + * Copyright 2014-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule Object.freeze + */ + +// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.3.9 +// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.freeze + +'use strict'; + +var nativeFreeze = typeof Object.freeze === 'function' && Object.freeze; + +function freeze(object) { + // ES5 and ES6 have different behaviors when object is not an Object. + // - ES5 Throw a TypeError + // - ES6 Return object + // Within React, using freeze() on a non-object is most likely to be an error + // so this method throws. + if (Object(object) !== object) { + throw new TypeError('freeze can only be called an Object'); + } + + // Freeze if possible, but don't error if it is not implemented. + if (nativeFreeze) { + nativeFreeze(object); + } + + return object; +} + +module.exports = freeze; diff --git a/src/shared/stubs/__tests__/Object.create-test.js b/src/shared/stubs/__tests__/Object.create-test.js new file mode 100644 index 00000000000..b8caf46681b --- /dev/null +++ b/src/shared/stubs/__tests__/Object.create-test.js @@ -0,0 +1,57 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails react-core + */ + +'use strict'; + +require('mock-modules') + .dontMock('Object.create'); + +var create; + +describe('Object.create', function() { + + beforeEach(function() { + create = require('Object.create'); + }); + + it('should throw when the prototype is null', function() { + expect(function() { + create(null); + }).toThrow( + 'This create() implementation cannot create empty objects with create(null)' + ); + }); + + it('should throw when the prototype is not an object', function() { + expect(function() { + create(1); + }).toThrow( + 'Object prototype may only be an Object' + ); + }); + + it('should throw when properties are given', function() { + expect(function() { + create({}, {}); + }).toThrow( + 'This create() implementation does not support assigning properties' + ); + }); + + it('should create an object that inherits from prototype', function() { + var proto = {}; + var object = create(proto); + + proto.foo = 'bar'; + expect(object.foo).toBe('bar'); + }); + +}); diff --git a/src/shared/stubs/__tests__/Object.freeze-test.js b/src/shared/stubs/__tests__/Object.freeze-test.js new file mode 100644 index 00000000000..4ea8e7a003b --- /dev/null +++ b/src/shared/stubs/__tests__/Object.freeze-test.js @@ -0,0 +1,76 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @emails react-core + */ + +'use strict'; + +require('mock-modules') + .dontMock('Object.freeze'); + +var freeze; + +var runnerCanFreeze = false; +try { + var obj = {foo: 'bar'}; + Object.freeze(obj); + try { + obj.foo = 'baz'; + runnerCanFreeze = obj.foo === 'bar'; + } catch (e) { + if (e instanceof TypeError) { + runnerCanFreeze = true; + } + } +} catch (x) {} + +describe('Object.freeze', function() { + + beforeEach(function() { + freeze = require('Object.freeze'); + }); + + it('should not throw when the argument is an object', function() { + expect(function() { + freeze({}); + }).not.toThrow(); + }); + + it('throws if the argument is not an object', function() { + expect(function() { + freeze(1); + }).toThrow( + 'freeze can only be called an Object' + ); + }); + + it('should return the same object it is given', function() { + var obj = {}; + + var returnValue = freeze(obj); + + expect(returnValue).toBe(obj); + }); + + it('should freeze the object if the native Object.freeze can', function() { + if ( !runnerCanFreeze ) { + pending(); + return; + } + + var obj = {foo: 'bar'}; + freeze(obj); + try { + obj.foo = 'baz'; + } catch (x) {} + + expect(obj.foo).toBe('bar'); + }) + +}); diff --git a/src/shared/vendor/core/emptyObject.js b/src/shared/vendor/core/emptyObject.js index c33476b05d4..a52b3124e3f 100644 --- a/src/shared/vendor/core/emptyObject.js +++ b/src/shared/vendor/core/emptyObject.js @@ -11,10 +11,12 @@ "use strict"; +var freeze = require('Object.freeze'); + var emptyObject = {}; if (__DEV__) { - Object.freeze(emptyObject); + freeze(emptyObject); } module.exports = emptyObject;