Lets start with answering these questions first. Answers are added at the bottom of this page.
Tricky questions on 'this'
1. What does this refer to in this code?
const obj = {
name: "Alice",
greet: () => console.log(this.name),
};
obj.greet();
2. What's the output?
function test() {
console.log(this === window);
}
const obj = { test };
obj.test.call(null);
3. What does this.x equal in the callback?
const obj = {
x: 10,
getX: function () {
return function () {
return this.x;
};
},
};
const fn = obj.getX();
console.log(fn());
4. What's logged here?
class Parent {
name = "parent";
getName = () => this.name;
}
class Child extends Parent {
name = "child";
}
const child = new Child();
const fn = child.getName;
console.log(fn());
5. What happens with this event handler?
const obj = {
count: 0,
increment() {
this.count++;
},
setup() {
document.addEventListener("click", this.increment);
},
};
// After clicking, what is obj.count?
6. What's the value of this in the IIFE?
const obj = {
value: 42,
getValue: function () {
return (function () {
return this.value;
})();
},
};
console.log(obj.getValue());
7. What does this log in strict mode vs non-strict?
function outer() {
function inner() {
console.log(this);
}
inner();
}
outer();
8. What's the output?
const obj1 = {
name: "obj1",
greet() {
console.log(this.name);
},
};
const obj2 = { name: "obj2" };
const greet = obj1.greet.bind(obj2);
greet.call(obj1);
9. What does this refer to?
setTimeout(
function () {
console.log(this);
}.bind({ name: "bound" }),
0
);
10. What's logged after the timeout?
const obj = {
name: "test",
delayedGreet() {
setTimeout(() => console.log(this.name), 100);
},
};
const fn = obj.delayedGreet;
fn();
11. What happens here?
function Constructor() {
this.value = 42;
return { value: 100 };
}
const instance = new Constructor();
console.log(instance.value);
12. What's the value of this.x?
const obj = {
x: 1,
getX: function () {
const inner = {
x: 2,
getInnerX: () => this.x,
};
return inner.getInnerX();
},
};
console.log(obj.getX());
13. What does this destructuring assignment do to this?
const obj = {
name: "Alice",
greet() {
console.log(`Hello, ${this.name}`);
},
};
const { greet } = obj;
greet();
14. What's logged in this getter?
const obj = {
_value: 10,
get value() {
return (() => this._value)();
},
};
console.log(obj.value);
15. What happens with this method chain?
const calculator = {
value: 0,
add(n) {
this.value += n;
return this;
},
multiply(n) {
this.value *= n;
return this;
},
};
const { add } = calculator;
const result = add.call({ value: 5 }, 3).multiply(2);
16. What's this in the nested function?
const obj = {
name: "outer",
method() {
function nested() {
console.log(this.name);
}
nested.call(this);
},
};
obj.method();
17. What happens with this async function?
const obj = {
name: "async",
async getName() {
return this.name;
},
};
const fn = obj.getName;
fn().then(console.log);
18. What's the output?
function test() {
console.log(this instanceof test);
}
test();
new test();
19. What does this equal in the array method?
const obj = {
numbers: [1, 2, 3],
sum: 0,
calculate() {
this.numbers.forEach(function (num) {
this.sum += num;
});
return this.sum;
},
};
console.log(obj.calculate());
20. What's the final value?
const obj = {
value: 1,
getValue() {
return this.value;
},
};
const getValue = obj.getValue.bind(obj).bind({ value: 2 });
console.log(getValue());
These questions test understanding of arrow functions, binding, call/apply, constructors, strict mode, event handlers, async functions, destructuring, and method chaining with this. Most developers get several of these wrong!
21. What's the output with this proxy?
const target = { name: "target" };
const handler = {
get(target, prop) {
return function () {
return this.name;
};
},
};
const proxy = new Proxy(target, handler);
console.log(proxy.getName());
22. What happens with eval and this?
const obj = {
name: "eval test",
test() {
return eval("this.name");
},
};
const fn = obj.test;
console.log(fn());
23. What's logged in this template literal?
const obj = {
name: "template",
greet: function () {
return `Hello ${(() => this.name)()}`;
},
};
const { greet } = obj;
console.log(greet());
24. What's this in the symbol method?
const sym = Symbol("test");
const obj = {
name: "symbol",
[sym]() {
return this.name;
},
};
const fn = obj[sym];
console.log(fn());
25. What happens with this with statement?
const obj1 = { name: "obj1" };
const obj2 = {
name: "obj2",
test() {
with (obj1) {
return this.name;
}
},
};
console.log(obj2.test());
26. What's the output with computed property?
const methodName = "greet";
const obj = {
name: "computed",
[methodName]: function () {
return this.name;
},
};
const fn = obj[methodName];
console.log(fn());
27. What happens in this try-catch?
const obj = {
name: "error",
test() {
try {
throw new Error();
} catch (e) {
return (() => this.name)();
}
},
};
console.log(obj.test());
28. What's this in the default parameter?
const obj = {
name: "default",
greet(greeting = this.name) {
return greeting;
},
};
const fn = obj.greet;
console.log(fn());
29. What happens with this generator?
const obj = {
name: "generator",
*getName() {
yield this.name;
},
};
const gen = obj.getName();
console.log(gen.next().value);
30. What's the output with Object.defineProperty?
const obj = {};
Object.defineProperty(obj, "test", {
get: function () {
return this;
},
set: function (val) {
this.value = val;
},
});
const getter = Object.getOwnPropertyDescriptor(obj, "test").get;
console.log(getter() === obj);
31. What happens with rest parameters?
const obj = {
name: "rest",
test(...args) {
return args.map((fn) => fn.call(this));
},
};
const result = obj.test(() => this.name);
console.log(result[0]);
32. What's this in the callback within map?
const obj = {
multiplier: 2,
process: function (arr) {
return arr.map(function (x) {
return x * this.multiplier;
}, this);
},
};
console.log(obj.process([1, 2, 3]));
33. What happens with this conditional operator?
const obj1 = {
name: "obj1",
fn() {
return this.name;
},
};
const obj2 = {
name: "obj2",
fn() {
return this.name;
},
};
const result = true ? obj1.fn : obj2.fn;
console.log(result());
34. What's the output with Reflect.apply?
const obj = { name: "reflect" };
function getName() {
return this.name;
}
console.log(Reflect.apply(getName, obj, []));
console.log(Reflect.apply(getName, null, []));
35. What happens in this module pattern?
const module = (function () {
const name = "module";
return {
getName: () => this.name,
getNameRegular: function () {
return this.name;
},
};
})();
console.log(module.getName());
console.log(module.getNameRegular());
36. What's this in the tagged template literal?
const obj = {
name: "tagged",
tag(strings, ...values) {
return this.name + strings[0];
},
};
const fn = obj.tag;
console.log(fn`Hello`);
37. What happens with optional chaining?
const obj = {
name: "optional",
nested: {
getName: function () {
return this.name;
},
},
};
const result = obj.nested?.getName();
console.log(result);
38. What's the output with Function.prototype.apply.call?
const obj = { name: "complex" };
function test() {
return this.name;
}
console.log(Function.prototype.apply.call(test, obj, []));
39. What happens with this class field initialization?
class Test {
name = "class";
arrow = () => this.name;
regular() {
return this.name;
}
static getThis() {
return this.name;
}
}
const t = new Test();
const { arrow, regular } = t;
console.log(arrow());
console.log(regular());
console.log(Test.getThis());
40. What's this in the finally block?
const obj = {
name: "finally",
test() {
try {
return "try";
} finally {
console.log(this.name);
}
},
};
const fn = obj.test;
fn();
Yes! I've missed several important categories. Here are 20 more questions covering the remaining tricky this scenarios:
41. What's this in the WeakMap callback?
const wm = new WeakMap();
const obj = { name: "weakmap" };
wm.set(obj, function () {
return this.name;
});
const fn = wm.get(obj);
console.log(fn());
42. What happens with this in a recursive function?
const obj = {
count: 0,
countdown: function (n) {
this.count++;
return n > 0 ? this.countdown(n - 1) : this.count;
},
};
const fn = obj.countdown;
console.log(fn(3));
43. What's this when using new.target?
function Test() {
console.log(this === new.target);
console.log(new.target);
}
new Test();
Test();
44. What happens with this in a closure inside a loop?
const obj = {
name: "closure",
methods: [],
};
for (let i = 0; i < 3; i++) {
obj.methods[i] = function () {
return this.name + i;
};
}
const fn = obj.methods[0];
console.log(fn());
45. What's this in an immediately destructured method?
const obj = {
name: "destructure",
greet() {
return this.name;
},
};
const result = (({ greet } = obj), greet());
console.log(result);
46. What happens with this in Object.create?
const proto = {
name: "proto",
getName() {
return this.name;
},
};
const obj = Object.create(proto);
obj.name = "instance";
const fn = obj.getName;
console.log(fn());
47. What's this in a method assigned via bracket notation?
const obj = { name: "bracket" };
obj["method"] = function () {
return this.name;
};
const fn = obj["method"];
console.log(fn());
48. What happens with this in super() calls?
class Parent {
constructor() {
this.type = "parent";
}
getType() {
return this.type;
}
}
class Child extends Parent {
constructor() {
super();
this.type = "child";
}
getParentType() {
return super.getType();
}
}
const child = new Child();
const fn = child.getParentType;
console.log(fn());
49. What's this when using Function constructor?
const obj = { name: "function constructor" };
const fn = new Function("return this.name");
console.log(fn.call(obj));
console.log(fn());
50. What happens with this in a mixin?
const Mixin = {
getName() {
return this.name;
},
};
const obj = { name: "mixed" };
Object.assign(obj, Mixin);
const fn = obj.getName;
console.log(fn());
51. What's this in a private method?
class Test {
name = "private";
#privateMethod() {
return this.name;
}
getPrivate = () => this.#privateMethod();
getPrivateRegular() {
return this.#privateMethod();
}
}
const t = new Test();
const fn1 = t.getPrivate;
const fn2 = t.getPrivateRegular;
console.log(fn1());
console.log(fn2());
52. What happens with this and property getters in inheritance?
class Parent {
get value() {
return this.name;
}
}
class Child extends Parent {
name = "child";
}
const child = new Child();
const getter = Object.getOwnPropertyDescriptor(Parent.prototype, "value").get;
console.log(getter.call(child));
53. What's this in a curried function?
const obj = {
name: "curried",
add: function (a) {
return function (b) {
return this.name + (a + b);
};
},
};
const fn = obj.add(1);
console.log(fn(2));
54. What happens with this in array destructuring with methods?
const obj = {
name: "array dest",
methods: [
function () {
return this.name;
},
() => this.name,
],
};
const [fn1, fn2] = obj.methods;
console.log(fn1());
console.log(fn2());
55. What's this when using call on an arrow function?
const obj1 = { name: "obj1" };
const obj2 = {
name: "obj2",
arrow: () => this.name,
regular: function () {
return this.name;
},
};
console.log(obj2.arrow.call(obj1));
console.log(obj2.regular.call(obj1));
56. What happens with this in a method returned from another method?
const obj = {
name: "method factory",
createMethod() {
const self = this;
return {
arrow: () => this.name,
regular: function () {
return this.name;
},
closure: function () {
return self.name;
},
};
},
};
const methods = obj.createMethod();
console.log(methods.arrow());
console.log(methods.regular());
console.log(methods.closure());
57. What's this in a static arrow function?
class Test {
static name = "static";
static arrow = () => this.name;
static regular() {
return this.name;
}
}
const arrow = Test.arrow;
const regular = Test.regular;
console.log(arrow());
console.log(regular());
58. What happens with this and Proxy traps?
const target = { name: "target" };
const handler = {
apply: function (target, thisArg, args) {
return thisArg.name;
},
};
function fn() {
return this.name;
}
const proxy = new Proxy(fn, handler);
console.log(proxy.call({ name: "proxy" }));
59. What's this in a method with spread operator?
const obj = { name: "spread" };
const methods = {
test: function () {
return this.name;
},
};
Object.assign(obj, { ...methods });
const fn = obj.test;
console.log(fn());
60. What happens with this in async generator?
const obj = {
name: "async gen",
async *generate() {
yield this.name;
yield await Promise.resolve(this.name);
},
};
const gen = obj.generate();
// What does this.name refer to in the generator?
Top comments (0)