March 21, 2024
TestsSource
- creating a bare object give it prototype
- is there a minimum prototype
- clarifications
- the prototype
Source Code
The source code we wrote during the event.
test.js
import {strict as assert} from 'assert';
describe('creating a bare object give it prototype', () => {
it('instantiating `Object(null)` has the prototype `undefined`', () => {
const instance = new Object(null);
assert.equal(instance.prototype, undefined);
});
it('`null` does not strictly equal `undefined`', () => {
assert.notEqual(null, undefined);
});
it('instantiating `Object()` has the prototype `undefined`', () => {
const instance = new Object();
assert.equal(instance.prototype, undefined);
});
it('instantiating an object with {x: 1} the prototype is `undefined`', () => {
const instance = new Object({x: 1});
assert.equal(instance.prototype, undefined);
});
it('demonstrating instantiating a function that has a prototype', () => {
function Klass() {}
Klass.prototype = {
x: function() { return 42; }
};
const instance = new Klass();
assert.equal(instance.x(), 42);
});
it('instantiate a function with a prototype and we can set a class property inside the constructor', () => {
function Klass() {
this.a = 1;
}
Klass.prototype = {
b: function() { return 2; }
}
const instance = new Klass();
assert.equal(instance.a, 1);
assert.equal(typeof instance.b, 'function');
});
it('we can get the prototype via __proto__ or Reflect.getPrototypeOf()', () => {
function Klass() {}
Klass.prototype = {};
const instance = new Klass();
assert(instance.__proto__ === Klass.prototype);
assert(Reflect.getPrototypeOf(instance) === Klass.prototype);
});
it('get the prototype like above, just for a new class', () => {
class Klass {}
const instance = new Klass();
assert(instance.__proto__ === Klass.prototype);
assert(Reflect.getPrototypeOf(instance) === Klass.prototype);
});
});
describe('is there a minimum prototype', () => {
it('setting the prototype to null reports an empty prototype', () => {
class Klass {}
Reflect.setPrototypeOf(Klass, null);
const instance = new Klass();
assert.deepEqual(Reflect.getPrototypeOf(instance), {});
});
it('an instance of Object.create(null) has a prototype of null', () => {
const obj = Object.create(null);
assert.equal(Object.getPrototypeOf(obj), null);
});
});
//
// Additions I have to make AFTER the meetup, to clarify some things we had been struggling with and didn't find the right answers to
//
describe('clarifications', () => {
describe('the prototype', () => {
// https://tc39.es/ecma262/#sec-function-instances-prototype
// Function instances that can be used as a constructor have a "prototype" property.
// Whenever such a Function instance is created another ordinary object is also created
// and is the initial value of the function's "prototype" property. Unless otherwise specified,
// the value of the "prototype" property is used to initialize the [[Prototype]] internal slot
// of the object created when that function is invoked as a constructor.
// [[construct]] does the following:
// OrdinaryCreateFromConstructor(newTarget, "%Object.prototype%").
// which does the following, with `intrinsicDefaultProto` being `%Object.prototype%` (see above):
// https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
// It creates an ordinary object whose [[Prototype]] value is retrieved from a constructor's "prototype" property, if it exists.
// Otherwise the intrinsic named by intrinsicDefaultProto is used for [[Prototype]]
// https://tc39.es/ecma262/#sec-built-in-function-objects
// The initial value of a built-in function object's [[Prototype]] internal slot is %Function.prototype%
it('the prototype of the constructor (which is just a function) is the function prototype', () => {
function KlassWithoutPrototype() {}
assert.equal(Reflect.getPrototypeOf(KlassWithoutPrototype), Function.prototype);
assert.equal(KlassWithoutPrototype.__proto__, Function.prototype);
});
it('the prototype of the instance points to `TheClass.prototype`', () => {
function KlassWithoutPrototype() {}
const instance = new KlassWithoutPrototype();
assert.equal(Reflect.getPrototypeOf(instance), KlassWithoutPrototype.prototype);
assert.equal(instance.__proto__, KlassWithoutPrototype.prototype); // the depreacted way
});
it('if no prototype is given, then an ordinary object is used', () => {
function KlassWithoutPrototype() {}
const instance = new KlassWithoutPrototype();
const instancesPrototype = Reflect.getPrototypeOf(instance); // this one is just an ordinary object, so test for this below
assert.equal(KlassWithoutPrototype.prototype instanceof Object, true);
assert.equal(Reflect.getPrototypeOf(instancesPrototype), Object.prototype);
assert.equal(instancesPrototype instanceof Object, true);
});
});
});
// inheritance explained:
// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
// All ordinary objects have an internal slot called [[Prototype]]. The value of this internal slot is either null
// or an object and is used for implementing inheritance. Assume a property named P is missing from an ordinary object O
// but exists on its [[Prototype]] object. If P refers to a data property on the [[Prototype]] object,
// O inherits it for get access, making it behave as if P was a property of O. If P refers to a writable data property
// on the [[Prototype]] object, set access of P on O creates a new data property named P on O.
// If P refers to a non-writable data property on the [[Prototype]] object, set access of P on O fails. If P refers
// to an accessor property on the [[Prototype]] object, the accessor is inherited by O for both get access and set access.