Js tips I can't remember

3 minute read

__proto__ VS prototype

__proto__ is the actual object that is used in the lookup chain to resolve methods and others. prototype is the object that is used to build __proto__ when creating an object with new.

The “cool kids” in JavaScript would generally pronounce __proto__ as “dunder proto”.

https://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript

1( new Foo ).__proto__ === Foo.prototype; // true
2( new Foo ).prototype === undefined; // true

{} VS Object.create(null)

Object.create(null) can create a ‘pure’ empty object that is without the delegation to Object.prototype.

1let obj1 = {};
2let obj2 = Object.create(null);
3console.log(obj1.__proto__); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
4console.log(obj2.__proto__); // undefined

Computed Property Names

ES6 adds computed property names, which is helpful when declaring objects using the object-literal syntax.

1var prefix = "foo";
2
3var myObject = {
4	[prefix + "bar"]: "hello",
5	[prefix + "baz"]: "world"
6};
7
8myObject["foobar"]; // hello
9myObject["foobaz"]; // world

Dangerous Function.prototype.name comparison

Sometimes we may spectify a function by comparing the function name:

1function Foo() {};
2let foo = new Foo();
3
4if (foo.constructor.name === 'Foo') {
5  console.log("'foo' is an instance of 'Foo'");
6} else {
7  console.log('Oops!');
8}

It may behave unexpectedly after building because most build tools compress the code to forms like:

1function a() {};
2let b = new a();
3if (b.constructor.name === 'Foo') {
4  console.log("'foo' is an instance of 'Foo'");
5} else {
6  console.log('Oops!');
7}

A more robust way of performing object property check

Normally we use hasOwnProperty() checks to see if object has the property or not. But consider objects created by:

1// this will not consult the [[Prototype]] chain
2let obj = Object.create(null)

Such object does not link to Object.prototype, thus obj.hasOwnProperty(...) would fail and raise error.

We can use a more robust way to perform property check:

1Object.prototype.hasOwnProperty.call(obj,"a") //false

Or:

1// this will check the current object or any higher level of the [[Prototype]] chain
2"a" in obj

for...in vs for...of

The for..in loop iterates over the list of enumerable properties on an object (including its [[Prototype]] chain).The for...of iterate over the values directly instead of the array indices (or object properties). It asks built-in or custom @@iterator of the thing to be iterated.

 1let arr = ['a', 'b', 'c']
 2arr.name = 'foo'
 3
 4arr // ["a", "b", "c", name: "foo"]
 5
 6for (key in arr)
 7  console.log(key) // 0 1 2 name
 8
 9for (value of arr)
10  console.log(value) // a b c

“(Prototypal) Inheritance”

When writing “prototype style” code like implementing Parent-Child class inheritance:

 1function Foo(name) {
 2	this.name = name;
 3}
 4
 5Foo.prototype.myName = function() {
 6	return this.name;
 7};
 8
 9function Bar(name,label) {
10	Foo.call( this, name );
11	this.label = label;
12}
13
14// Then trying to build a family with Foo and Bar
15// ...

A common mis-conception/confusion here is that either of the following approaches would also work, but they do not work as you’d expect:

1// doesn't work like you want!
2Bar.prototype = Foo.prototype;
3
4// works kinda like you want, but with
5// side-effects you probably don't want :(
6Bar.prototype = new Foo();

the right way is:

1// "make a new 'Bar dot prototype' object that's linked to 'Foo dot prototype'."
2// pre-ES6
3// throws away default existing `Bar.prototype`
4Bar.prototype = Object.create( Foo.prototype );
5
6// ES6+
7// modifies existing `Bar.prototype`
8Object.setPrototypeOf( Bar.prototype, Foo.prototype );

reference: You-Dont-Know-JS: “(Prototypal) Inheritance”