JS PollyFillls Machine Coding Interview
This blog will give you the experience of how Pollyfills questions are asked in the interviews.
In this blog you will get an understanding of how to approach a PollyFill implementation question in an interview. I will include proper examples of some popular questions which which will help you relate the different scenarios of how and what to answer in the interview.
1. Array.prototype.includes
(Easy)
๐ป Implement a polyfill for Array.prototype.includes
without using indexOf
or built-in array methods.
if( !Array.prototype.myIncludes){
Array.prototype.myIncludes = function(searchElement , startIndex = 0){
startIndex = startIndex>=0 ? startIndex: Math.max( 0, this.length-Math.abs(startIndex));
for( let i = startIndex; i< this.length; i++){
if( this[i] == searchElement || ( Number.isNaN(this[i]) && Number.isNaN(searchElement)) ){
return true;
}
// if (Object.is(this[i], searchElement)) return true; (Alternative)
}
return false;
}
}
โ Conceptual Questions:
How does
Array.prototype.includes
differ fromArray.prototype.indexOf
?Answer
includes
returnstrue
orfalse
depending on whether the element exists in the array, whileindexOf
returns the index of the first occurrence or-1
if the element is not found.includes
performs a strict equality check (===
), but it correctly identifiesNaN
, whereasindexOf
does not becauseNaN !== NaN
.indexOf
is useful for getting the position of an element, whileincludes
is better for simple existence checks.
Why does
Array.prototype.includes
returntrue
forNaN
, whileindexOf
does not?Answer
indexOf
relies on strict equality (===
), andNaN !== NaN
, soindexOf(NaN)
always returns-1
.includes
uses theSameValueZero
algorithm, which treatsNaN
as equal toNaN
.
How does
startIndex
work when it is negative?Answer
If
startIndex
is negative then we have to search the element fromthis.length-startIndex
to the end of the array. If thestartIndex
is very low value then we will have to search the entire array.const arr = [10, 20, 30, 40, 50]; console.log(arr.includes(30, -2)); // false (starts searching from index 3)
2. Object.assign (Medium)
โ Conceptual Questions:
What is
Object.assign
used for?Object.assign
is used to copy enumerable own properties from one or more source objects to a target object.It performs a shallow copy, meaning nested objects are still referenced rather than deeply copied.
It is commonly used for merging objects, creating object clones, and adding properties dynamically.
What happens if
Object.assign
receivesnull
orundefined
as a target or source?If the target is
null
orundefined
, it throws aTypeError
because assignment requires an object.If a source is
null
orundefined
, it is ignored, and no properties are copied.
Object.assign({}, null, { a: 1 }); // { a: 1 }
Object.assign(null, { a: 1 }); // TypeError
How does
Object.assign
handle inherited properties?Object.assign
only copies own properties of source objects, not properties from the prototype chain.It copies both string keys and symbol keys, but only if they belong to the object itself.
const proto = { inherited: 'I am inherited' };
const obj = Object.create(proto);
obj.own = 'I am ram';
const copy = Object.assign({}, obj);
console.log(copy); // { own: 'I am ram' } (inherited property is not copied)
๐ป Implement a polyfill for Object.assign
if( !Object.myAssign){
Object.myAssign = function(target, ...sources){
if(target == null)
throw new TypeError("target cannot be null as we cannot convert null/undefined to an object. Pass {} as target in the calling function.")
let res = Object(target);
for( let source of sources){
if( source != null){
// In the object there can be keys / Symbols / Inherited properties
// Object.assign only copies the keys and symbols and not inherited properties.
for(let key of Object.keys(source) ){
res[key] = source[key];
}
for(let symbol of Object.getOwnPropertySymbols(sources)){
res[symbol] = source[symbol];
}
}
}
return res;
}
}
const obj1 = { a: 1 };
const obj2 = { b: 2, [Symbol('c')]: 3 };
const obj3 = Object.create({ inherited: 4 }, { d: { value: 5, enumerable: true } });
console.log(Object.myAssign({}, obj1, obj2, obj3));
// { a: 1, b: 2, d: 5, [Symbol(c)]: 3 }
Bonus :
Read about Enumerable Properties
How Properties Become Enumerable or Not?
Properties defined using object literals (
{}
) orObject.defineProperty()
:By default, properties in object literals are enumerable.
If defined using
Object.defineProperty()
, you must explicitly setenumerable: true
if you want them to be enumerable.
const obj1 = { x: 10 }; // enumerable by default
const obj2 = {};
Object.defineProperty(obj2, 'y', { value: 20, enumerable: false });
console.log(Object.keys(obj1)); // ['x']
console.log(Object.keys(obj2)); // [] (because 'y' is non-enumerable)
Built-in properties and methods (e.g., from prototypes) are usually non-enumerable:
- Many built-in properties, like
length
in arrays and methods on prototypes, are non-enumerable.
- Many built-in properties, like
console.log(Object.keys([])); // [] (does not include 'length')
console.log([].propertyIsEnumerable('length')); // false
Symbol properties are not enumerable by default in
Object.keys()
andfor...in
:- But they can be retrieved using
Object.getOwnPropertySymbols()
.
- But they can be retrieved using
const sym = Symbol('hidden');
const obj = { visible: 1, [sym]: 2 };
console.log(Object.keys(obj)); // ['visible'] (Symbol property is skipped)
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(hidden)]
Summary
Method | Includes Enumerable Properties? | Includes Non-Enumerable? | Includes Symbols? |
Object.keys(obj) | โ Yes | โ No | โ No |
Object.values(obj) | โ Yes | โ No | โ No |
Object.entries(obj) | โ Yes | โ No | โ No |
Object.getOwnPropertyNames(obj) | โ Yes | โ Yes | โ No |
Object.getOwnPropertySymbols(obj) | โ No | โ No | โ Yes |
for...in | โ Yes (own + inherited) | โ No | โ No |
๐ Enumerable properties are those that appear when iterating with for...in
or Object.keys()
, unless explicitly made non-enumerable using Object.defineProperty()
! ๐