Illegal invocation on window['document']['body']['append'] function

(I think) I have tried every .bind , .call() or .apply version,

But I can't make F2("World!") work

var F1 = window["document"]["body"];

// works fine:
F1["append"]("Hello")

var F2 = F1["append"];

// valid function append() 
console.log( F2 );

// Illegal invocation
F2("World!");

Would like to get it working in:


let D=["document", "body", "append"];
([0,1,2].reduce((root, x) => root[D[x]], window))('World!');

Upvotes: 3

Views: 153

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 371193

F1["append"] is only a reference to Element.prototype.append:

var F1 = window["document"]["body"];
var F2 = F1["append"];
console.log(F2 === Element.prototype.append);

This function requires a calling context - a this value - in order for the function to know what the parent element is, to which the child will be appended to. With

F1["append"]("Hello")

The object on which the function is called is F1, the this value that the function sees, so everything works as expected. But with

F2("World!");

There is no calling context - the function being called is not called as part of an object, and the function hasn't been bound, so the internal Element.prototype.append doesn't know which parent element the child should be appended to.

.bind works, as does .call, as does making a function which calls F1 with the proper calling context:

var F1 = window["document"]["body"];

// works fine:
F1["append"]("Hello")

var F2 = F1["append"];
F2.call(F1, "World!");

var F1 = window["document"]["body"];

// works fine:
F1["append"]("Hello")

var F2 = child => F1["append"](child);
F2("World!");

To do this with reduce, first .pop off the last property. Then use reduce to get to the next to last property (the object on which the function should be called on). Then you can call the function with the required calling context via bracket notation:

const paths = ["document", "body", "append"];
const fnProp = paths.pop();
const lastObj = paths.reduce((parent, nextProp) => parent[nextProp], window);
lastObj[fnProp]('World');

Upvotes: 2

Related Questions