Skip to content

Instantly share code, notes, and snippets.

@felthy
Last active December 15, 2015 15:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felthy/5283131 to your computer and use it in GitHub Desktop.
Save felthy/5283131 to your computer and use it in GitHub Desktop.
Test to see what happens when you freeze an object's prototype via `Object.freeze`.

This Gist is a test to see what happens when you freeze an object's prototype via Object.freeze.

I created it because, after I upgraded my NodeJS installation to version 0.10.2 (I previously had v0.8.18), the Pipette library stopped working with this error:

Fatal error: Cannot assign to read only property 'onClose' of #<State>
TypeError: Cannot assign to read only property 'onClose' of #<State>
    at new State (node_modules/pipette/lib/sink.js:81:16)

The problem is that the library freezes the prototypes of various objects, but the objects want to override some of the properties they inherit from their prototypes which is prevented because the prototype is frozen:

    function State(emitter, source) {
        // This line causes the error
        this.onClose = this.onClose.bind(this);
    }

    State.prototype.onClose = function onClose(info) {
        ...
    }

    Object.freeze(State);
    Object.freeze(State.prototype);

I'd have thought that would be ok, because the instance is not itself frozen.

So, this gist is a minimal test case to see what happens in the above example in various browsers and in NodeJS.

Running the tests

NodeJS

Just execute the fiddle.js file under node:

node fiddle.js

In a browser

To run in a browser, this Gist can be run as a JSFiddle:

http://jsfiddle.net/gh/gist/library/pure/5283131/

Results

At time of writing, results are:

Chrome and NodeJS

Only tests 5 and 6 fail.

Safari and Firefox

All tests fail except test 4, the one that uses Object.defineProperty().

/* Empty file */
<h1 id="qunit-header">Unit Tests</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"></div>
"use strict";
if (typeof QUnit === 'undefined') {
var assert = require("assert");
global.test = function(name, callback) {
assert.doesNotThrow(callback);
};
global.equal = assert.equal;
global.ok = assert.ok;
}
test('Frozen prototype with string property only', function() {
var Clazz = function() {
this.prop1 = 'instance';
};
Clazz.prototype.prop1 = 'initial';
Object.freeze(Clazz.prototype);
var instance = new Clazz();
ok(Object.isExtensible(instance), "instance should be extensible");
ok(!Object.isExtensible(Clazz.prototype), "prototype should not be extensible");
equal(false, Object.getOwnPropertyDescriptor(Clazz.prototype, 'prop1').writable, "prototype property 'prop1' should not be writable");
equal(true, instance.hasOwnProperty('prop1'), "instance should have own property 'prop1'");
equal(true, Object.getOwnPropertyDescriptor(instance, 'prop1').writable, "instance property 'prop1' should be writable");
equal('instance', instance.prop1);
});
test('Frozen prototype with function property, set to string in instance', function() {
var Clazz = function() {
this.prop1 = 'instance';
};
Clazz.prototype.prop1 = function() { return 'initial'; };
Object.freeze(Clazz.prototype);
var instance = new Clazz();
equal('instance', instance.prop1);
});
test('Frozen prototype with two function properties, set both to strings in instance', function() {
var Clazz = function() {
this.prop1 = 'instance';
this.prop2 = 'instance';
};
Clazz.prototype.prop1 = function() { return 'initial'; };
Clazz.prototype.prop2 = function() { return 'initial'; };
Object.freeze(Clazz.prototype);
var instance = new Clazz();
equal('instance', instance.prop1);
equal('instance', instance.prop2);
});
test('Frozen prototype with two function properties, set to string and function in instance via Object.defineProperty()', function() {
var Clazz = function() {
Object.defineProperty(this, 'prop1', { value: 'instance', enumerable: true, writable: true });
Object.defineProperty(this, 'prop2', { value: function() { return 'instance'; }, enumerable: true, writable: true });
};
Clazz.prototype.prop1 = function() { return 'initial'; };
Clazz.prototype.prop2 = function() { return 'initial'; };
Object.freeze(Clazz.prototype);
var instance = new Clazz();
equal('instance', instance.prop1);
equal('instance', instance.prop2());
});
test('Frozen prototype with two function properties, set to string and function in instance via assignment', function() {
var Clazz = function() {
this.prop1 = 'instance';
this.prop2 = function() { return 'instance'; };
};
Clazz.prototype.prop1 = function() { return 'initial'; };
Clazz.prototype.prop2 = function() { return 'initial'; };
Object.freeze(Clazz.prototype);
var instance = new Clazz();
equal('instance', instance.prop1);
equal('instance', instance.prop2());
});
test('Frozen prototype with single function property, set to function in instance via assignment', function() {
var Clazz = function() {
this.prop1 = function() { return 'instance'; };
};
Clazz.prototype.prop1 = function() { return 'initial'; };
Object.freeze(Clazz.prototype);
var instance = new Clazz();
equal('instance', instance.prop1());
});
name: Object Freeze Prototype Behaviour
description: Test for quirks in the behaviour of Object.freeze() when applied to an object prototype
authors:
- Pete Feltham
resources:
- http://code.jquery.com/qunit/git/qunit.css
- http://code.jquery.com/qunit/git/qunit.js
normalize_css: no
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment