Reputation: 1193
I recognize that some JavaScript objects have internal slot properties, denoted in the specification by double square brackets [[ ]], which can only be accessed by methods.
For example:
var str = new String('example');
When inspecting variable str
, I see that the internal slot [[PrimitiveValue]] has the value 'example'
, which I cannot access directly, because it's internal. The only way to access it by the method toString()
.
How can I implement a custom object with that behavior, with a custom internal property like [[MyField]] and a custom method to access that property?
Upvotes: 2
Views: 504
Reputation: 122966
I don't think you can create real internal properties/slots.
From 262.ecma-international.org:
The actual semantics of objects, in ECMAScript, are specified via algorithms called internal methods. Each object in an ECMAScript engine is associated with a set of internal methods that defines its runtime behaviour. These internal methods are not part of the ECMAScript language. They are defined by this specification purely for expository purposes (emph added KooiInc).
You can however create objects with internal values accessible by methods you define - using a factory function. For example:
function myNumbersFactory() {
return function(defaultValue = 0) {
const internalHistory = [];
const internalDefaultValue = defaultValue;
return Object.freeze({
set value(val) {
const nr = parseInt(val);
internalHistory.push(isNaN(nr) ? internalDefaultValue : nr); },
get value() { return internalHistory.slice(-1).pop(); },
get default() { return `default value ${internalDefaultValue}`; },
get history() { return [...internalHistory]; }
});
};
}
const numbers = myNumbersFactory();
const myNumbers = numbers();
const myNumbers2 = numbers(42);
// myNumbers
let i = 0;
myNumbers.value = `will be set to internalDefaultValue (0)`;
while(++i < 6) { myNumbers.value = i; }
myNumbers.value = 42;
console.log(`* myNumbers\n${
Object.keys(myNumbers)
.map(k => `${k}: ${myNumbers[k]}`).join(`\n`)}`);
// myNumbers2
i = -1;
while(++i < 6) { myNumbers2.value = i; }
myNumbers2.value = `will be set to internalDefaultValue (42)`;
console.log(`* myNumbers2\n${
Object.keys(myNumbers2)
.map(k => `${k}: ${myNumbers2[k]}`).join(`\n`)}`);
.as-console-wrapper {
max-height: 100% !important;
}
Upvotes: 1
Reputation: 1133
The [[PrimitiveValue]]
is an internal property and you can't interact with it from your code. But you can emulate the behavior of the String
class by overriding the toPrimitive
method:
var str = {
[Symbol.toPrimitive]: (hint) => {
return 'example';
}
};
console.log(`${str}`); // 'example'
The toPrimitive
method is defined in EcmaScript 6.0, 7.1.1. It is called with one parameter called hint
which can be 'default'
, 'string'
or 'number'
. You can return different values depending on the hint to have an object that behaves like a string and like a number:
var str = {
[Symbol.toPrimitive]: (hint) => {
if (hint === 'number') return 111;
return 'example';
}
};
console.log(`${str}`); // 'example'
console.log(str * 10); // 1110
Upvotes: 1
Reputation: 330
You can create a custom object in JavaScript with a private property and a method to access it that mimics the behavior of inner slots. For example:
function CustomObject(value) {
const primitiveValue = value;
this.getValue = function () {
return primitiveValue;
};
}
const obj = new CustomObject('example');
console.log(obj.getValue());
Here's the link to the documentation on this topic:
Private Variables in JavaScript
Upvotes: 0
Reputation: 2108
var SLOT = require('internal-slot');
var assert = require('assert');
var o = {};
assert.throws(function () { SLOT.assert(o, 'foo'); });
assert.equal(SLOT.has(o, 'foo'), false);
assert.equal(SLOT.get(o, 'foo'), undefined);
SLOT.set(o, 'foo', 42);
assert.equal(SLOT.has(o, 'foo'), true);
assert.equal(SLOT.get(o, 'foo'), 42);
assert.doesNotThrow(function () { SLOT.assert(o, 'foo'); });
Upvotes: 0