Since it’s birth 26 years ago at Netscape, JavaScript has come a long way. A language which was used only to interact with Java applets and do simple DOM manipulation is now used for writing back-ends and desktop and mobile applications too. Ecosystem grew by a big margin as well as the community. Just like every other language, JavaScript had (and still has) rough edges and quirks. We’re stuck with some of them because of the backward compatibility. Some are, (un)fortunately, mostly or completely gone. Some of these can still be used but it’s highly discouraged.
Object.prototype.watch
and Object.prototype.unwatch
methods
Unce upon a time there was an easy way to watch for the property changes on an object.
var cat = {};
cat.watch("name", function (propertyName, previousValue, newValue) {
return "Mr. " + newValue;
});
cat.name = "Oswald";
console.log("Hello " + cat.name + "!"); // Hello Mr. Oswald!
cat.unwatch("name");
cat.name = "Luna";
console.log("Hello " + cat.name + "!"); // Hello Luna!
Alternative
Nowadays you can use Proxy
for this purpose.
const handler = {
set: (obj, prop, value) => {
if (prop === 'name') {
obj[prop] = `Mr. ${value}`;
}
}
};
let cat = new Proxy({}, handler);
cat.name = "Oswald";
console.log("Hello " + cat.name + "!"); // Hello Mr. Oswald!
cat = { ...cat }; // this will remove behavior imposed by Proxy
cat.name = "Luna";
console.log("Hello " + cat.name + "!"); // Hello Luna!
with
statement
We all know how horrible working with long chain of properties can be. Fortunately, there is a way around it. Unfortunately – you shouldn’t use it.
const cat = {
details: {
passport: {
location: {
city: 'New York'
}
}
}
};
with (cat.details.passport.location) {
city = 'Meowyork';
}
There are two reasons why you shouldn’t use with
statement.
- There is no place for optimization inside it, since you can’t predict if variable will refer to a property or to an outside variable.
- It violates lexical scope, making program analysis very hard or even infeasible.
Also, it will be impossible for you to use it in ES6+ (or ES5 with the strict mode turned on). Strict mode prohibits it’s usage.
Alternative
The best you can do is to declare a variable which will hold the chain of properties instead.
const cat = {
details: {
passport: {
location: {
city: 'New York'
}
}
}
};
const catLocation = cat.details.passport.location;
catLocation.city = 'Meowyork';
Expression closures
Long before arrow functions were even in a plan, there were expression closures. They allowed you to omit curly braces and return statements from the method definitions completely.
var cat = function() "Luna";
var favorites = {
food: function() "Tuna"
};
Alternative
This has been removed in favor of using standard ES syntax.
var cat = function() { return "Luna"; }
var favorites = {
food: function() { return "Tuna"; }
};
Nowadays you can also use arrow functions and method definitions (both introduced in ES6).
const cat = () => "Luna";
const favorites = {
get food() { return "Tuna"; }
};
Object.observe
and Object.unobserve
methods
Back in the days there was also an easy way of getting an information about any changes to an object.
var cat = {
name: "Oswald"
};
Object.observe(cat, function(changes) {
console.log(changes);
});
cat.name = "Luna";
// [{ name: 'name', object: <obj>, type: 'update', oldValue: 'Oswald' }]
Object.unobserve(cat);
cat.name = "Max";
There were the similar methods for arrays too – Array.observe
and Array.unobserve
.
Alternative
You can do this with Proxy
too.
const cat = new Proxy({ name: "Oswald" }, {
get: (target, prop) => {
console.log({ type: "get", target, prop });
return Reflect.get(target, prop);
},
set: (target, prop, value) => {
console.log({ type: "set", target, prop, value });
return Reflect.set(target, prop, value);
}
});
cat.name = "Luna";
// { type: 'set', target: <obj>, prop: 'name', value: 'Luna' }
cat.name;
// { type: 'get', target: <obj>, prop: 'name' }
let
expressions and let
blocks
In ES6, two statements for declaring block-scoped variables have been introduced; let
and const
. For the brief period of time, there were non-standard extensions to the let statement. These were let
expressions and let
blocks.
let
blocks provided a way to instantiate a block where variables can have different values, without affecting the same-named ones outside of that block.
var catName = "Oswald";
var catAge = 2.5;
let (catName = "Luna", catAge = 2) {
console.log(catName + "(" + catAge + " years old)"); // Luna (2 years old)
}
console.log(catName + "(" + catAge + " years old)"); // Oswald (2.5 years old)
let
expressions did the similar thing but on the expression level.
var catName = "Oswald";
let(catName = "Luna") console.log(catName); // Oswald
console.log(catName); // Luna
Alternative
Since let
is block scoped you can just declare variables again inside inner scope and change them there.
let catName = "Oswald";
let catAge = 2.5;
{
let catName = "Luna", catAge = 2;
console.log(catName + "(" + catAge + " years old)"); // Luna (2 years old)
}
console.log(catName + "(" + catAge + " years old)"); // Oswald (2.5 years old)
HTML wrapper methods on strings
They are basically bunch of methods which wrapped your string with tags like bold
, blink
, font
, small
, big
, i
etc.
"Some teeny-tiny text".fontsize(3); // <font size="3">Some teeny-tiny text.</font>
"Some tiny text.".small(); // <small>Some tiny text.</small>
"Some yuuuge text.".big(); // <big>Some yuuge text.</big>
"Talk to the hand!".bold(); // <b>Talk to the hand!</b>
"You have been terminated.".blink(); // <blink>You have been terminated.</blink>
Alternative
There are no alternatives for this monstrosity.
ParallelArray
This one was an experimental feature introduced by Mozilla in the Firefox (specifically, version 17 of the Gecko engine). It’s purpose was to enable data-parallelism by executing multiple functions in parallel. If it wasn’t possible, they would be executed in the sequential order.
var cats = new ParallelArray(["Oswald", "Luna", "Max"]);
cats.map(function(name) {
return "😸 " + cat;
});
Alternative
Today you can use Promise.all
to accomplish this.
Conclusion
It’s fantastic to see how much JavaScript has progressed for the last 26 years. Who could’ve thought that language made in 10 days can become one of the most dominant in the industry? I believe it’s a good practice to take a step back and see how things worked in the past. That can teach us not to repeat the same mistakes anymore. It can also make us more grateful for the things we have today. Even though I have a fair share of criticism for JavaScript, I can’t wait to see what’s coming in the next two decades.
Top comments (17)
Some example with
with
To watch Object change, you can also
Object.defineProperty
Great list, I didn't have a clue about some of those :)
One other way of doing a "with like thing" having a go at the C# syntax I've seen is this:
That's nice!
Yup, that's another way of doing it - although that will return
in the browser since
location
is a global property - we can rename it to something else though.Don't forget
let
andconst
are already block scoped, so a simple alternative to yourlet
block example is this:You're right, thanks - I'll update the article now.
I've to say JavaScript changed so much that its hard to keep track of what have changes... But
Proxy
thing is just blew my mind. I never knew it was available. Thanks a lotI only knew about with, which is used sometimes on code golfing 🙂 Nice article!
HTML wrapper methods on strings: "There are no alternatives for this monstrosity." Hahah 🤣 Wow, never knew they even exist tbh. Great stuff, thanks!
great post thanks .
👍🏽
Awesome article!