Protecting object properties against overwriting from console - javascript

I'm trying to protect a property of an object from being overwritten by the console. e.g. I have a person object, with a list of allergies as a property. The list of allergies should be able to be modified, however it should always be a list and an error should be thrown if a user tries writing 'person.allergies = "nonsense" '
I've tried looking into Object.freeze() and Object.seal() but cannot get these to work for this purpose, as I don't believe there is a way to unfreeze an object.
class Person {
constructor(name){
this.name = name
this.allergies = []
}
addAllergy(allergy){
this.allergies.push(allergy)
return allergy
}
}
ben = new Person('Ben') // Creating a new Object
ben.addAllergy('Dairy') // Giving the object a new allergy
ben.allergies // Should output ['Dairy']
ben.allergies = 'Soy' // Should make no changes to ben object.

You could make allergies a non writable property with Object.defineProperty:
class Person {
constructor(name){
this.name = name
Object.defineProperty(this, 'allergies', {
value: [],
writable: false
});
}
addAllergy(allergy){
this.allergies.push(allergy)
return allergy
}
}
ben = new Person('Ben') // Creating a new Object
ben.addAllergy('Dairy') // Giving the object a new allergy
console.log(ben.allergies) // Should output ['Dairy']
ben.allergies = 'Soy' // Should make no changes to ben object.
ben.addAllergy('Peanutes')
console.log(ben.allergies) // still array
writable defaults to false so you don't need to explicitly set it, but I think it makes the intention clearer. configurable also defaults to false which means you can't redefine the property with another call to Object.defineProperty().

Use private properties:
class Person {
#allergies;
constructor(name){
this.name = name
this.#allergies = []
}
addAllergy(allergy){
this.#allergies.push(allergy)
return allergy
}
get allergies() {
// you might want to deep clone it here
// to fully protect from changes the array behind
return this.#allergies;
}
set allergies(value) {
throw new Error('haha, I got you!');
}
}
Private fields are being implemented in the ECMA standard. You can
start using them today with babel 7 and stage 3 preset
Source

Related

copy instance of class in javascript / typescript

I have two objects inst1, inst2 which are both instances of the same class. If I use
inst2 = JSON.parse(JSON.stringify(inst1));
now if I change values of properties of inst2, values in inst1 do not change. That is great.
But sadly methods of inst2 have disappeared. So if I do
inst2.method1();
I get the error
"inst2.method1 is not a function"
Is there some way I can copy the values in an instance without destroying methods?
(obviously I could laboriously copy each value. I am trying to avoid that because I am lazy.)
I have tried to follow typescript - cloning object but I cannot make it work-
Ok, I have played a little since the provided answers are not 100% clear.
If you want to have a shallow copy and copy the methods too, you can use Object.create.
Again: If your object is simple enough, Object.create will be sufficient for you
const originalPerson = new Person("John");
originalPerson.address = new Address("Paris", "France");
const newPerson = Object.create(originalPerson);
/// this will be true
const isInstanceOf = newPerson instanceof Person;
//this will change the property of the new person ONLY
newPerson.name = "Peter";
//methods will work
newPerson.someMethod();
//methods will work even on nested objects instances
newPerson.address.anotherMethod();
// BUT if we change the city on any of the instances - will change the address.city of both persons since we have done a shallow copy
newPerson.address.city = "Berlin";
I have created typescript playground (just remove the types) to show it works and the drawback with its usage - link to the playground
Another approach is the class itself to have a clone method and to be responsible for its own cloning logic. An example follows, along with a link to another playground
class Address {
constructor(city, country) {
this.city = city;
this.country = country;
}
clone() {
// no special logic, BUT if the address eveolves this is the place to change the clone behvaiour
return Object.create(this);
}
getAddressDetails() {
return `City: ${this.city} country ${this.country}`;
}
}
class Person {
constructor(name, address) {
this.name = name;
this.address = address;
}
clone() {
const newInstance = Object.create(this);
//clone all other class instances
newInstance.address = this.address.clone();
return newInstance;
}
getPersonDetails() {
//calling internally address.getAddressDetails() ensures that the inner object methods are also cloned
return `This is ${this.name}, I live in ${this.address.getAddressDetails()}`
}
}
const originalAddress = new Address("Paris", "France");
const originalPerson = new Person("John", originalAddress);
const clonedPerson = originalPerson.clone();
clonedPerson.name = "Peter";
clonedPerson.address.city = "Berlin";
clonedPerson.address.country = "Germany";
// Log to console
console.log(`Original person: ${originalPerson.getPersonDetails()}`)
console.log(`Cloned person: ${clonedPerson.getPersonDetails()}`)
You should use structured cloning, see this answer:
How do I correctly clone a JavaScript object?
The reason that your current code isn't working is because you are parsing a stringified json object. Json stringify will remove all of the methods of an object and only stringify the objects values.
I came back to this at a convenient point and made quite a bit of progress by combining some of the above answers. The general purpose cloner was getting quite ugly (see below) and still not working (for arrays of class-objects) when I realised that it would be impossible to write a general purpose cloner.
I use the term class-object to mean an object defined by a class.
If a class-object contains a variable which itself is type class-object, call it subObj, then the general purpose cloner cannot know whether 1) it should copy subObj or 2) it should create a new instance of subObj and copy into the sub-properties. The answer depends on the meaning in the class.
In the first case above subObj. is just a pointer to another instance of subObj.
Therefore I strongly agree with the second part of Svetoslav Petkov's answer that the "class itself [should] have a clone method and be responsible for its own cloning logic.".
For what it's worth this is as far as I got with a general purpose cloner (in TypeScript). It is adapted from the other answers and creates new instances of class-objects liberally:
public clone(): any {
var cloneObj = new (this.constructor as any)() as any;
for (var attribut in this) {
// attribut is a string which will take the values of the names of the propertirs in 'this'
// And for example, if aNumber is a property of 'this' then
// this['aNumber'] is the same as this.aNumber
if (typeof this[attribut] === "object") {
let thisAttr = this[attribut] as any;
let cloneAttr = cloneObj[attribut] as any;
if (this[attribut] instanceof Array) {
for (let i in thisAttr) {
cloneAttr[i] = thisAttr[i]; // **** will not work on arrays of objects!!
}
continue; // to next attrib in this
}
if (this[attribut] instanceof Date) {
cloneAttr.setTime(thisAttr.getTime());
continue; // to next attrib in this
}
try {
cloneObj[attribut] = thisAttr.clone();
//cloneObj[attribut] = this.clone(); // with this, (from https://stackoverflow.com/questions/28150967/typescript-cloning-object) stack just gets bigger until overflow
}
catch (err) {
alert("Error: Object " + attribut + " does not have clone method." +
"\nOr " + err.message);
}
} else {
cloneObj[attribut] = this[attribut];
}
}
return cloneObj;
}

Is there a way to dynamically add getter to a class in javascript/typescript?

I need some guidance or expert knowledge on JavaScript capabilities.
I'm studying TypeScript ATM, specifically decorators functionality.
Is there a way to dynamically add a getter method to a Prototype object so that it is executed in place of the plain property access on an instance.
Here's some code for example:
class Car {
#decorate
color: string = 'red';
drive(): {
return 'Driving';
}
}
function decorate(target, key): void {
//would be cool to add a getter and update
//the prototype in target to contain such getter
//I know this won't work, but to get the idea.
target[key] = get function() {
console.log(`Accessing property: ${key}`);
return eval(`this.${key}`)
}
}
Then, when I would create and object and try to access .color
const car = new Car();
car.color;
ideally I would see at the console
Accessing property: color
You can use Proxy in JavaScript. As MDN states, it allows you to create an object that can be used in place of the original object, but which may redefine fundamental Object operations like getting, setting, and defining properties. Proxy objects are commonly used to log property accesses, validate, format, or sanitize inputs, and so on.
class Car {
color = 'red'
drive() {
return 'Driving'
}
}
const proxy = new Proxy(new Car(), {
get(target, key) {
console.log(`Accessing property: ${key}`);
return Reflect.get(target, key)
}
})
proxy.color // prints "Accessing property: color" and returns value of color.
In plain javascript you could use Object.defineProperty to dinamicaly add getters and setters to object.
That was my inital comment.
If you will to decorate only certain fields of the object then MDN example would be the easiest way to this properly:
const o = {a: 0};
Object.defineProperty(o, 'b', { get() { return this.a + 1; } });
console.log(o.b) // Runs the getter, which yields a + 1 (which is 1)

Disable property mutation in JS

I was creating a component and was trying to break my implementation. The idea is to not allow user to manipulate the exposed properties.
The implementation was like this:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data; },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
As you see, setting the property is handled but user is still able to mutate it using .push. This is because I'm returning a reference.
I have handled this case like:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data.slice(); },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
As you see, I'm returning a new array to solve this. Not sure how it will affect performance wise.
Question: Is there an alternate way to not allow user to mutate properties that are of type object?
I have tried using writable: false, but it gives me error when I use it with get.
Note: I want this array to mutable within class but not from outside.
Your problem here is that you are effectively blocking attempts to modify MyClass. However, other objects members of MyClass are still JavaScript objects. That way you're doing it (returning a new Array for every call to get) is one of the best ways, though of course, depending of how frequently you call get or the length of the array might have performance drawbacks.
Of course, if you could use ES6, you could extend the native Array to create a ReadOnlyArray class. You can actually do this in ES5, too, but you lose the ability to use square brackets to retrieve the value from a specific index in the array.
Another option, if you can avoid Internet Explorer, is to use Proxies (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy).
With a proxy, you can trap calls to get properties of an object, and decide what to return or to do.
In the example below, we create a Proxy for an array. As you see in the handler, we define a get function. This function will be called whenever the value of a property of the target object is accessed. This includes accessing indexes or methods, as calling a method is basically retrieving the value of a property (the function) and then calling it.
As you see, if the property is an integer number, we return that position in the array. If the property is 'length' then we return the length of the array. In any other case, we return a void function.
The advantage of this is that the proxyArray still behaves like an array. You can use square brackets to get to its indexes and use the property length. But if you try to do something like proxyArray.push(23) nothing happens.
Of course, in a final solution, you might want decide what to do based on which
method is being called. You might want methods like map, filter and so on to work.
And finally, the last advantage of this approach is that you keep a reference to the original array, so you can still modify it and its values are accessible through the proxy.
var handler = {
get: function(target, property, receiver) {
var regexp = /[\d]+/;
if (regexp.exec(property)) { // indexes:
return target[property];
}
if (property === 'length') {
return target.length;
}
if (typeof (target[property]) === 'function') {
// return a function that does nothing:
return function() {};
}
}
};
// this is the original array that we keep private
var array = [1, 2, 3];
// this is the 'visible' array:
var proxyArray = new Proxy(array, handler);
console.log(proxyArray[1]);
console.log(proxyArray.length);
console.log(proxyArray.push(32)); // does nothing
console.log(proxyArray[3]); // undefined
// but if we modify the old array:
array.push(23);
console.log(array);
// the proxy is modified
console.log(proxyArray[3]); // 32
Of course, the poblem is that proxyArray is not really an array, so, depending on how you plan to use it, this might be a problem.
What you want isn't really doable in JavaScript, as far as I'm aware. The best you can hope for is to hide the data from the user as best you can. The best way to do that would be with a WeakMap
let privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, {
data: []
});
}
addEntry(entry) {
privateData.get(this).data.push(entry);
}
getData() {
return privateData.get(this).data.concat();
}
}
So long as you never export privateData don't export from the module, or wrap within an IIFE etc.) then your MyClass instances will be able to access the data but external forces can't (other than through methods you create)
var myInstance = new MyClass();
myInstance.getData(); // -> []
myInstance.getData().push(1);
myInstance.getData(); // -> []
myInstance.addEntry(100);
myInstance.getData(); // -> [100]

Javascript - getPrototypeOf doesn't return the parent prototype

I have the following Javascript snippet:`
var alien = {
kind: "alien"
};
var person = {
kind: "human"
};
var zack = Object.create(person);
alert(Object.getPrototypeOf(zack));
</script>
`
Not sure why it show zack's prototype is Object instead of person. Thanks
its also person .But person is an object.its not display with alert() try with console.log() .If you need to show with in alert use with JSON.stringify()
How can I view an object with an alert()
var alien = {
kind: "alien"
};
var person = {
kind: "human"
};
var zack = Object.create(person);
console.log(Object.getPrototypeOf(zack));
alert(JSON.stringify(Object.getPrototypeOf(zack)))
Give this a try. I cleaned up your code a bit and used the standard naming conventions of classes start with an Uppercase. Also using the console to log, alerts are so yesterday.
// Define the Alien class, always start with an uppercase
var Alien = function() {
};
// Setup it's prototype
Alien.prototype = {
kind: 'alien',
grab: function() {
return 'grabby grab';
}
}
// Define a Person class
var Person = function() {
this.kind = "human";
};
// Copy Alien prototype to Person
Person.prototype = Object.create(Alien.prototype);
// Create a new instance of of Person
var zack = new Person();
// See what's inside
console.log('Prototype function call: ' + zack.grab());
console.log('Zack is a: ' + zack.kind);
console.log('Zack is secretly a: ' + Object.getPrototypeOf(zack).kind);
console.log(Object.getPrototypeOf(zack));
The reason you see [object Object] is because when you alert Object.getPrototypeOf(zack), the object needs to convert into string to be shown in the alert. Now, default toString() method Object.prototype.toString is being called that returns the string representation of an object. If you want to really display some meaningful text in the alert, you need to implement toString method on the person object itself. Doing this would show your own text implemented in toString() function. The reason why this would happen is the way function lookup happens in javascript. First, object is looked into, then it's prototype and then further into prototype chain. Since in this case lookup would succeed at the object level itself, Object.prototype.toString function won't be called and you would see your own text in alert box.
var alien = {
kind: "alien"
};
var person = {
kind: "human"
};
person.toString = function () {
return "I am '"+this.kind+"' kind of object";
}
var zack = Object.create(person);
alert(Object.getPrototypeOf(zack));
The screen shot is showing the text returned by the Object.prototype.toString method. The method is called automatically if an object, constructed by Object, needs to be converted to text:
console.log( {} + "" ); // automatic convertion
console.log( {}.toString() ); // explicit convertion
What it is not saying is that the prototype of zack is Object. If you want more information about an object you can use JSON.stringify to create a string listing of all its non-function valued properties and their values. (JSON.stringify ignores function valued properties by design.)
You could also use equality operators to test if one object is the same as another, e.g.:
var alien = {
kind: "alien"
};
var person = {
kind: "human"
};
var zack = Object.create(person);
console.log( "zack is prototyped on person: "
+ (Object.getPrototypeOf( zack) === person)
);

how to use javascript Object.defineProperty

I looked around for how to use the Object.defineProperty method, but couldn't find anything decent.
Someone gave me this snippet of code:
Object.defineProperty(player, "health", {
get: function () {
return 10 + ( player.level * 15 );
}
})
But I don't understand it. Mainly, the get is what I can't get (pun intended). How does it work?
Since you asked a similar question, let's take it to step by step. It's a bit longer, but it may save you much more time than I have spent on writing this:
Property is an OOP feature designed for clean separation of client code. For example, in some e-shop you might have objects like this:
function Product(name,price) {
this.name = name;
this.price = price;
this.discount = 0;
}
var sneakers = new Product("Sneakers",20); // {name:"Sneakers",price:20,discount:0}
var tshirt = new Product("T-shirt",10); // {name:"T-shirt",price:10,discount:0}
Then in your client code (the e-shop), you can add discounts to your products:
function badProduct(obj) { obj.discount+= 20; ... }
function generalDiscount(obj) { obj.discount+= 10; ... }
function distributorDiscount(obj) { obj.discount+= 15; ... }
Later, the e-shop owner might realize that the discount can't be greater than say 80%. Now you need to find EVERY occurrence of the discount modification in the client code and add a line
if(obj.discount>80) obj.discount = 80;
Then the e-shop owner may further change his strategy, like "if the customer is reseller, the maximal discount can be 90%". And you need to do the change on multiple places again plus you need to remember to alter these lines anytime the strategy is changed. This is a bad design. That's why encapsulation is the basic principle of OOP. If the constructor was like this:
function Product(name,price) {
var _name=name, _price=price, _discount=0;
this.getName = function() { return _name; }
this.setName = function(value) { _name = value; }
this.getPrice = function() { return _price; }
this.setPrice = function(value) { _price = value; }
this.getDiscount = function() { return _discount; }
this.setDiscount = function(value) { _discount = value; }
}
Then you can just alter the getDiscount (accessor) and setDiscount (mutator) methods. The problem is that most of the members behave like common variables, just the discount needs special care here. But good design requires encapsulation of every data member to keep the code extensible. So you need to add lots of code that does nothing. This is also a bad design, a boilerplate antipattern. Sometimes you can't just refactor the fields to methods later (the eshop code may grow large or some third-party code may depend on the old version), so the boilerplate is lesser evil here. But still, it is evil. That's why properties were introduced into many languages. You could keep the original code, just transform the discount member into a property with get and set blocks:
function Product(name,price) {
this.name = name;
this.price = price;
//this.discount = 0; // <- remove this line and refactor with the code below
var _discount; // private member
Object.defineProperty(this,"discount",{
get: function() { return _discount; },
set: function(value) { _discount = value; if(_discount>80) _discount = 80; }
});
}
// the client code
var sneakers = new Product("Sneakers",20);
sneakers.discount = 50; // 50, setter is called
sneakers.discount+= 20; // 70, setter is called
sneakers.discount+= 20; // 80, not 90!
alert(sneakers.discount); // getter is called
Note the last but one line: the responsibility for correct discount value was moved from the client code (e-shop definition) to the product definition. The product is responsible for keeping its data members consistent. Good design is (roughly said) if the code works the same way as our thoughts.
So much about properties. But javascript is different from pure Object-oriented languages like C# and codes the features differently:
In C#, transforming fields into properties is a breaking change, so public fields should be coded as Auto-Implemented Properties if your code might be used in the separately compiled client.
In Javascript, the standard properties (data member with getter and setter described above) are defined by accessor descriptor (in the link you have in your question). Exclusively, you can use data descriptor (so you can't use i.e. value and set on the same property):
accessor descriptor = get + set (see the example above)
get must be a function; its return value is used in reading the property; if not specified, the default is undefined, which behaves like a function that returns undefined
set must be a function; its parameter is filled with RHS in assigning a value to property; if not specified, the default is undefined, which behaves like an empty function
data descriptor = value + writable (see the example below)
value default undefined; if writable, configurable and enumerable (see below) are true, the property behaves like an ordinary data field
writable - default false; if not true, the property is read only; attempt to write is ignored without error*!
Both descriptors can have these members:
configurable - default false; if not true, the property can't be deleted; attempt to delete is ignored without error*!
enumerable - default false; if true, it will be iterated in for(var i in theObject); if false, it will not be iterated, but it is still accessible as public
* unless in strict mode - in that case JS stops execution with TypeError unless it is caught in try-catch block
To read these settings, use Object.getOwnPropertyDescriptor().
Learn by example:
var o = {};
Object.defineProperty(o,"test",{
value: "a",
configurable: true
});
console.log(Object.getOwnPropertyDescriptor(o,"test")); // check the settings
for(var i in o) console.log(o[i]); // nothing, o.test is not enumerable
console.log(o.test); // "a"
o.test = "b"; // o.test is still "a", (is not writable, no error)
delete(o.test); // bye bye, o.test (was configurable)
o.test = "b"; // o.test is "b"
for(var i in o) console.log(o[i]); // "b", default fields are enumerable
If you don't wish to allow the client code such cheats, you can restrict the object by three levels of confinement:
Object.preventExtensions(yourObject) prevents new properties to be added to yourObject. Use Object.isExtensible(<yourObject>) to check if the method was used on the object. The prevention is shallow (read below).
Object.seal(yourObject) same as above and properties can not be removed (effectively sets configurable: false to all properties). Use Object.isSealed(<yourObject>) to detect this feature on the object. The seal is shallow (read below).
Object.freeze(yourObject) same as above and properties can not be changed (effectively sets writable: false to all properties with data descriptor). Setter's writable property is not affected (since it doesn't have one). The freeze is shallow: it means that if the property is Object, its properties ARE NOT frozen (if you wish to, you should perform something like "deep freeze", similar to deep copy - cloning). Use Object.isFrozen(<yourObject>) to detect it.
You don't need to bother with this if you write just a few lines fun. But if you want to code a game (as you mentioned in the linked question), you should care about good design. Try to google something about antipatterns and code smell. It will help you to avoid situations like "Oh, I need to completely rewrite my code again!", it can save you months of despair if you want to code a lot. Good luck.
get is a function that is called when you try to read the value player.health, like in:
console.log(player.health);
It's effectively not much different than:
player.getHealth = function(){
return 10 + this.level*15;
}
console.log(player.getHealth());
The opposite of get is set, which would be used when you assign to the value. Since there is no setter, it seems that assigning to the player's health is not intended:
player.health = 5; // Doesn't do anything, since there is no set function defined
A very simple example:
var player = {
level: 5
};
Object.defineProperty(player, "health", {
get: function() {
return 10 + (player.level * 15);
}
});
console.log(player.health); // 85
player.level++;
console.log(player.health); // 100
player.health = 5; // Does nothing
console.log(player.health); // 100
defineProperty is a method on Object which allow you to configure the properties to meet some criterias.
Here is a simple example with an employee object with two properties firstName & lastName and append the two properties by overriding the toString method on the object.
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
employee.toString=function () {
return this.firstName + " " + this.lastName;
};
console.log(employee.toString());
You will get Output as : Jameel Moideen
I am going to change the same code by using defineProperty on the object
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
value: function () {
return this.firstName + " " + this.lastName;
},
writable: true,
enumerable: true,
configurable: true
});
console.log(employee.toString());
The first parameter is the name of the object and then second parameter is name of the property we are adding , in our case it’s toString and then the last parameter is json object which have a value going to be a function and three parameters writable,enumerable and configurable.Right now I just declared everything as true.
If u run the example you will get Output as : Jameel Moideen
Let’s understand why we need the three properties such as writable,enumerable and configurable.
writable
One of the very annoying part of the javascript is , if you change the toString property to something else for example
if you run this again , everything gets breaks.
Let’s change writable to false. If run the same again you will get the correct output as ‘Jameel Moideen’ . This property will prevent overwrite this property later.
enumerable
if you print all the keys inside the object , you can see all the properties including toString.
console.log(Object.keys(employee));
if you set enumerable to false , you can hide toString property from everybody else. If run this again you will get firstName,lastName
configurable
if someone later redefined the object on later for example enumerable to true and run it. You can see toString property came again.
var employee = {
firstName: "Jameel",
lastName: "Moideen"
};
Object.defineProperty(employee, 'toString', {
value: function () {
return this.firstName + " " + this.lastName;
},
writable: false,
enumerable: false,
configurable: true
});
//change enumerable to false
Object.defineProperty(employee, 'toString', {
enumerable: true
});
employee.toString="changed";
console.log(Object.keys(employee));
you can restrict this behavior by set configurable to false.
Orginal reference of this information is from my personal Blog
Basically, defineProperty is a method that takes in 3 parameters - an object, a property, and a descriptor. What is happening in this particular call is the "health" property of the player object is getting assigned to 10 plus 15 times that player object's level.
yes no more function extending for setup setter & getter
this is my example Object.defineProperty(obj,name,func)
var obj = {};
['data', 'name'].forEach(function(name) {
Object.defineProperty(obj, name, {
get : function() {
return 'setter & getter';
}
});
});
console.log(obj.data);
console.log(obj.name);
Object.defineProperty() is a global function..Its not available inside the function which declares the object otherwise.You'll have to use it statically...
Summary:
Object.defineProperty(player, "health", {
get: function () {
return 10 + ( player.level * 15 );
}
});
Object.defineProperty is used in order to make a new property on the player object. Object.defineProperty is a function which is natively present in the JS runtime environemnt and takes the following arguments:
Object.defineProperty(obj, prop, descriptor)
The object on which we want to define a new property
The name of the new property we want to define
descriptor object
The descriptor object is the interesting part. In here we can define the following things:
configurable <boolean>: If true the property descriptor may be changed and the property may be deleted from the object. If configurable is false the descriptor properties which are passed in Object.defineProperty cannot be changed.
Writable <boolean>: If true the property may be overwritten using the assignment operator.
Enumerable <boolean>: If true the property can be iterated over in a for...in loop. Also when using the Object.keys function the key will be present. If the property is false they will not be iterated over using a for..in loop and not show up when using Object.keys.
get <function> : A function which is called whenever is the property is required. Instead of giving the direct value this function is called and the returned value is given as the value of the property
set <function> : A function which is called whenever is the property is assigned. Instead of setting the direct value this function is called and the returned value is used to set the value of the property.
Example:
const player = {
level: 10
};
Object.defineProperty(player, "health", {
configurable: true,
enumerable: false,
get: function() {
console.log('Inside the get function');
return 10 + (player.level * 15);
}
});
console.log(player.health);
// the get function is called and the return value is returned as a value
for (let prop in player) {
console.log(prop);
// only prop is logged here, health is not logged because is not an iterable property.
// This is because we set the enumerable to false when defining the property
}
import { CSSProperties } from 'react'
import { BLACK, BLUE, GREY_DARK, WHITE } from '../colours'
export const COLOR_ACCENT = BLUE
export const COLOR_DEFAULT = BLACK
export const FAMILY = "'Segoe UI', sans-serif"
export const SIZE_LARGE = '26px'
export const SIZE_MEDIUM = '20px'
export const WEIGHT = 400
type Font = {
color: string,
size: string,
accent: Font,
default: Font,
light: Font,
neutral: Font,
xsmall: Font,
small: Font,
medium: Font,
large: Font,
xlarge: Font,
xxlarge: Font
} & (() => CSSProperties)
function font (this: Font): CSSProperties {
const css = {
color: this.color,
fontFamily: FAMILY,
fontSize: this.size,
fontWeight: WEIGHT
}
delete this.color
delete this.size
return css
}
const dp = (type: 'color' | 'size', name: string, value: string) => {
Object.defineProperty(font, name, { get () {
this[type] = value
return this
}})
}
dp('color', 'accent', COLOR_ACCENT)
dp('color', 'default', COLOR_DEFAULT)
dp('color', 'light', COLOR_LIGHT)
dp('color', 'neutral', COLOR_NEUTRAL)
dp('size', 'xsmall', SIZE_XSMALL)
dp('size', 'small', SIZE_SMALL)
dp('size', 'medium', SIZE_MEDIUM)
export default font as Font
Defines a new property directly on an object, or modifies an existing property on an object, and return the object.
Note: You call this method directly on the Object constructor rather
than on an instance of type Object.
const object1 = {};
Object.defineProperty(object1, 'property1', {
value: 42,
writable: false, //If its false can't modify value using equal symbol
enumerable: false, // If its false can't able to get value in Object.keys and for in loop
configurable: false //if its false, can't able to modify value using defineproperty while writable in false
});
Simple explanation about define Property.
Example code: https://jsfiddle.net/manoj_antony32/pu5n61fs/
Object.defineProperty(Array.prototype, "last", {
get: function() {
if (this[this.length -1] == undefined) { return [] }
else { return this[this.length -1] }
}
});
console.log([1,2,3,4].last) //returns 4

Categories

Resources