Alternative way to get "C" letter in jsfuck

Viewed 2181

Currently jsfuck use following code to get "C" character

console.log(
    Function("return escape")()(("")["italics"]())[2],
)
   
console.log(  // after expansion
    []["flat"]["constructor"]("return escape")()(([]+[])["italics"]())[!![]+!![]]
)

console.log(  // after final strings expansion we get pure jsfuck code
    [][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]][[]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[][[]])[+!![]]+([]+![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])[!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+!![])[+!![]]]([]+([]+!![])[+!![]]+([]+!![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[!![]+!![]]+([]+!![])[+!![]]+([]+[][[]])[+!![]]+(+[![]]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[+([]+(+!![])+(+!![]))]+([]+!![])[!![]+!![]+!![]]+([]+![])[!![]+!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+![])[+!![]]+(+([]+(!![]+!![])+(!![]+!![]+!![]+!![]+!![])))[[]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[])[[]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[][[]])[+!![]]+([]+![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])[!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+!![])[+!![]]][[]+([]+[][[]])[+!![]]+([]+![])[+!![]]+((+[])[[]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[][[]])[+!![]]+([]+![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])[!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+!![])[+!![]]]+[])[[]+(+!![])+(+!![])]+([]+!![])[!![]+!![]+!![]]]](+([]+(!![]+!![]+!![])+(+[])))+([]+!![])[!![]+!![]+!![]])()(([]+[])[[]+([]+[][[]])[!![]+!![]+!![]+!![]+!![]]+([]+!![])[+![]]+([]+![])[+!![]]+([]+![])[!![]+!![]]+([]+[][[]])[!![]+!![]+!![]+!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+![])[!![]+!![]+!![]]]())[!![]+!![]]
)

But this method use deprecated function "".italics (info here). I develop little tool and try to find some alternative based on btoa but I saddly discovered that this is not supported by node.js (online)

console.log(
  Function("return btoa")()("t.")[1]
)

console.log( // after expansion
  []["flat"]["constructor"]("return btoa")()("t.")[+!![]]
)

console.log( // after full expansion
  [][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]][[]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+[][[]])[+!![]]+([]+![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+!![])[!![]+!![]]+([]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[!![]+!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+!![])[+!![]]]([]+([]+!![])[+!![]]+([]+!![])[!![]+!![]+!![]]+([]+!![])[+![]]+([]+!![])[!![]+!![]]+([]+!![])[+!![]]+([]+[][[]])[+!![]]+(+[![]]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[+([]+(+!![])+(+!![]))]+([][[]+([]+!![])[!![]+!![]+!![]]+([]+[][[]])[+!![]]+([]+!![])[+![]]+([]+!![])[+!![]]+([]+[][[]])[!![]+!![]+!![]+!![]+!![]]+([]+!![])[!![]+!![]+!![]]+([]+![])[!![]+!![]+!![]]]()+[])[!![]+!![]]+([]+!![])[+![]]+(!![]+[][[]+([]+![])[+[]]+([]+![])[!![]+!![]]+([]+![])[+!![]]+([]+!![])[+![]]])[[]+(+!![])+(+[])]+([]+![])[+!![]])()([]+([]+!![])[+![]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]])[+!![]]
)

Is there a way (working on current versions of chrome, safari, firefox and node.js) to get character "C" using jsfuck but without using deprecated methods?

UPDATE 2022

According to this comment and this doc btoa is supported by node-js at least in version 17 - this allow to use short (and more trivial) solutions (one is given in this question - in above snippet) (you can check this online here)

6 Answers

The fact that escape is semi-deprecated kept bothering me, so I took another stab at it. Let's rebuild JSFuck from scratch.

Level 0

You can get the following values as primitives:

false           ![]
true            !![]
undefined       [][[]]
NaN             +[![]]
""              []+[]
0               +[]
1               +!+[]
2               +!+[]+!+[]
3               +!+[]+!+[]+!+[]
4               +!+[]+!+[]+!+[]+!+[]
5               +!+[]+!+[]+!+[]+!+[]+!+[]
6               +!+[]+!+[]+!+[]+!+[]+!+[]+!+[]
7               +!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]
8               +!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]
9               +!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]

Level 1

With the above values and the fact that value+[] converts to string, we can set up the following character substitution rules:

"0"             0+[]
"1"             1+[]
"2"             2+[]
"3"             3+[]
"4"             4+[]
"5"             5+[]
"6"             6+[]
"7"             7+[]
"8"             8+[]
"9"             9+[]
"a"             (false+[])[1]
"d"             (undefined+[])[2]
"e"             (true+[])[3]
"f"             (false+[])[0]
"i"             ([false]+undefined)[1+[0]]
"l"             (false+[])[2]
"n"             (undefined+[])[1]
"r"             (true+[])[1]
"s"             (false+[])[3]
"t"             (true+[])[0]
"u"             (undefined+[])[0]
"N"             (NaN+[])[0]

Level 2

With the above characters, we can construct these four strings:

"11e100"        +!+[]+[+!+[]]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]
"1e1000"        +!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]]
"flat"          (![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]
"entries"       (true+[])[3]+(undefined+[])[1]+(true+[])[0]+(true+[])[1]+([false]+undefined)[1+[0]]+(true+[])[3]+(false+[])[3]

With which we can get three more values:

1.1e+101                +("11e100")
Infinity                +("1e1000")
Array Iterator          []["entries"]()
Array.prototype.flat    []["flat"]

The last one is particularly useful, because when converted to a string, it yields this:

"function flat() {\n    [native code]\n}"

Or this:

"function flat() { [native code] }"

This is a bit wonky to work with, but the characters up to and including the { are always the same, as is the last character.

Array Iterator will convert to something more stable:

"[object Array Iterator]"

This gives us more characters to work with:

" "             ([false]+[]["flat"])[2+[0]]
"("             ([]+[]["flat"])[1+[3]]
")"             ([]+[]["flat"])[1+[4]]
"+"             (+("11e100")+[])[4]
"."             (+("11e100")+[])[1]
"["             ([]+[]["entries"]())[0]
"]"             ([]+[]["entries"]())[2+[2]]
"{"             ([true]+[]["flat"])[2+[0]]
"c"             ([]["flat"]+[])[3]
"j"             ([]+[]["entries"]())[3]
"o"             ([true]+[]["flat"])[1+[0]]
"y"             (true+[Infinity])[1+[1]]
"A"             ([NaN]+([]+[]["entries"]()))[1+[1]]
"I"             (Infinity+[])[0]

Level 3

Combining level 1 and 2 characters and values, we can now build three new strings:

".0000001"      (+("11e100")+[])[1]+[0]+[0]+[0]+[0]+[0]+[0]+[1]
"constructor"   ([]["flat"]+[])[3]+([true]+[]["flat"])[1+[0]]+(undefined+[])[1]+(false+[])[3]+(true+[])[0]+(true+[])[1]+(undefined+[])[0]+([]["flat"]+[])[3]+(true+[])[0]+([true]+[]["flat"])[1+[0]]+(true+[])[1]

And this gives us access to a bunch of further values:

1e-7            +(".0000001")
Boolean         (![])["constructor"]
Number          (+[])["constructor"]
String          ([]+[])["constructor"]
Function        []["flat"]["constructor"]

By converting to strings, we get yet more characters:

"-"             (+(".0000001")+[])[2]
"b"             ([]+(+[])["constructor"])[1+[2]]
"g"             (false+[0]+([]+[])["constructor"])[2+[0]]
"m"             ([]+(+[])["constructor"])[1+[1]]
"B"             ([NaN]+(![])["constructor"])[1+[2]]
"F"             ([NaN]+[]["flat"]["constructor"])[1+[2]]
"S"             ([NaN]+([]+[])["constructor"])[1+[2]]

Level 4

Given the uppercase S, we could now build the string "toString manually. However, if we first build the string "name", we can achieve an overall shorter code:

"name"         (undefined+[])[1]+(false+[])[1]+([]+(+[])["constructor"])[1+[1]]+(true+[])[3]
"toString"     (true+[])[0]+([true]+[]["flat"])[1+[0]]+([]+[])["constructor"]["name"]

And with that we can call Number.toString(), giving us all remaining lowercase letters:

"h"             (+(1+[0]+[1]))["toString"](2+[1])[1]
"k"             (+(2+[0]))["toString"](2+[1])
"p"             (+(2+[1]+[1]))["toString"](3+[1])[1]
"q"             (+(2+[1]+[2]))["toString"](3+[1])[1]
"v"             (+(3+[1]))["toString"](3+[2])
"w"             (+(3+[2]))["toString"](3+[3])
"x"             (+(1+[0]+[1]))["toString"](3+[4])[1]
"z"             (+(3+[5]))["toString"](3+[6])

At the same time, we can construct two more strings:

"slice"         (false+[])[3]+(false+[])[2]+([false]+undefined)[1+[0]]+([]["flat"]+[])[3]+(true+[])[3]
"-1"            (+(".0000001")+[])[2]+[+!+[]]

And that gives us one last character that we need for the next level:

"}"             ([true]+[]["flat"])["slice"]("-1")

Level 5

At this point, there's one primitive we obtained that we haven't used yet: using Function as an eval primitive:

[]["flat"]["constructor"](...)()

Since we have all lowercase letter now as well as space, +, ., [, ], { and }, we can build:

"try{String().normalize(false)}catch(f){return f}"

By means of:

(true+[])[0]+(true+[])[1]+(true+[Infinity])[1+[1]]+([true]+[]["flat"])[2+[0]]+([]+[])["constructor"]["name"]+([]+[]["flat"])[1+[3]]+([]+[]["flat"])[1+[4]]+(+("11e100")+[])[1]+(undefined+[])[1]+([true]+[]["flat"])[1+[0]]+(true+[])[1]+([]+(+[])["constructor"])[1+[1]]+(false+[])[1]+(false+[])[2]+([false]+undefined)[1+[0]]+(+(3+[5]))["toString"](3+[6])+(true+[])[3]+([]+[]["flat"])[1+[3]]+![]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])["slice"]("-1")+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]["flat"]+[])[3]+(+(1+[0]+[1]))["toString"](2+[1])[1]+([]+[]["flat"])[1+[3]]+(false+[])[0]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])[2+[0]]+(true+[])[1]+(true+[])[3]+(true+[])[0]+(undefined+[])[0]+(true+[])[1]+(undefined+[])[1]+([false]+[]["flat"])[2+[0]]+(false+[])[0]+([true]+[]["flat"])["slice"]("-1")

Calling String.prototype.normalize() with a value that isn't a valid Unicode Normalization Form will throw a RangeError, which we catch and return to the caller. We thus have:

RangeError      []["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")()

Note that the above is an instance - we'd have to use ["constructor"] to get the function/constructor, but we can just convert it to string as-is, giving us two more uppercase letters:

"E"             ([false]+[]["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")())[1+[0]]
"R"             ([]+[]["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")())[0]

Level 6

With two more characters unlocked, we can now construct this string:

"return RegExp" (true+[])[1]+(true+[])[3]+(true+[])[0]+(undefined+[])[0]+(true+[])[1]+(undefined+[])[1]+([false]+[]["flat"])[2+[0]]+([]+[]["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")())[0]+(true+[])[3]+(false+[0]+([]+[])["constructor"])[2+[0]]+([false]+[]["flat"]["constructor"]("try{String().normalize(false)}catch(f){return f}")())[1+[0]]+(+(1+[0]+[1]))["toString"](3+[4])[1]+(+(2+[1]+[1]))["toString"](3+[1])[1]

And that gives us a new value/function:

RegExp          []["flat"]["constructor"]("return RegExp")()

When invoked with no arguments and converting the resulting RegExp to string, we get:

"/(?:)/"        []+[]["flat"]["constructor"]("return RegExp")()()

So we have a bunch of new special characters:

"/"             ([]+[]["flat"]["constructor"]("return RegExp")()())[0]
":"             ([]+[]["flat"]["constructor"]("return RegExp")()())[3]
"?"             ([]+[]["flat"]["constructor"]("return RegExp")()())[2]

Level 7

Now we feed one of those characters back into the regex to get a new string:

"/\\//"         []+RegExp("/")

This gives us access to a single new character:

"\\"            ([]+RegExp("/"))[1]

Level 8

Let's build a new string:

"try{Function([]+[[]].concat([[]]))()}catch(f){return f}"

By:

(true+[])[0]+(true+[])[1]+(true+[Infinity])[1+[1]]+([true]+[]["flat"])[2+[0]]+[]["flat"]["constructor"]["name"]+([]+[]["flat"])[1+[3]]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+(+("11e100")+[])[4]+([]+[]["entries"]())[0]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+([]+[]["entries"]())[2+[2]]+(+("11e100")+[])[1]+([]["flat"]+[])[3]+([true]+[]["flat"])[1+[0]]+(undefined+[])[1]+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]+[]["flat"])[1+[3]]+([]+[]["entries"]())[0]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+([]+[]["entries"]())[2+[2]]+([]+[]["flat"])[1+[4]]+([]+[]["flat"])[1+[4]]+([]+[]["flat"])[1+[3]]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])["slice"]("-1")+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]["flat"]+[])[3]+(+(1+[0]+[1]))["toString"](2+[1])[1]+([]+[]["flat"])[1+[3]]+(false+[])[0]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])[2+[0]]+(true+[])[1]+(true+[])[3]+(true+[])[0]+(undefined+[])[0]+(true+[])[1]+(undefined+[])[1]+([false]+[]["flat"])[2+[0]]+(false+[])[0]+([true]+[]["flat"])["slice"]("-1")

This is equivalent to:

"try{Function(',')()}catch(f){return f}"

Except for the fact that we can't write ',' (yet). Evaluating that will return a SyntaxError object, which, when converted to string, will yield:

"SyntaxError: Unexpected token ','"

We can then feed that string into RegExp("[\u0027]").exec(...)[0] to extract the single quote.
So we wanna run:

RegExp("[\u0027]").exec(Function("try{Function([]+[[]].concat([[]]))()}catch(f){return f}")())[0]

Applying a whole bunch of substitutions from above, we get one final character:

"'"             RegExp(([]+[]["entries"]())[0]+([]+RegExp("/"))[1]+(undefined+[])[0]+[+[]]+[+[]]+[+!+[]+!+[]]+[+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+[]["entries"]())[2+[2]])[(true+[])[3]+(+(1+[0]+[1]))["toString"](3+[4])[1]+(true+[])[3]+([]["flat"]+[])[3]]([]["flat"]["constructor"]((true+[])[0]+(true+[])[1]+(true+[Infinity])[1+[1]]+([true]+[]["flat"])[2+[0]]+[]["flat"]["constructor"]["name"]+([]+[]["flat"])[1+[3]]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+(+("11e100")+[])[4]+([]+[]["entries"]())[0]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+([]+[]["entries"]())[2+[2]]+(+("11e100")+[])[1]+([]["flat"]+[])[3]+([true]+[]["flat"])[1+[0]]+(undefined+[])[1]+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]+[]["flat"])[1+[3]]+([]+[]["entries"]())[0]+([]+[]["entries"]())[0]+([]+[]["entries"]())[2+[2]]+([]+[]["entries"]())[2+[2]]+([]+[]["flat"])[1+[4]]+([]+[]["flat"])[1+[4]]+([]+[]["flat"])[1+[3]]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])["slice"]("-1")+([]["flat"]+[])[3]+(false+[])[1]+(true+[])[0]+([]["flat"]+[])[3]+(+(1+[0]+[1]))["toString"](2+[1])[1]+([]+[]["flat"])[1+[3]]+(false+[])[0]+([]+[]["flat"])[1+[4]]+([true]+[]["flat"])[2+[0]]+(true+[])[1]+(true+[])[3]+(true+[])[0]+(undefined+[])[0]+(true+[])[1]+(undefined+[])[1]+([false]+[]["flat"])[2+[0]]+(false+[])[0]+([true]+[]["flat"])["slice"]("-1"))())[0]

Level 9

At this point, we can return every character we want simply by doing:

Function("return '\uXXXX'")()

Demo

Let's take the character "C" from your question:

Function("return '\u0043'")()

Running this through all the substitutions above produces an absolute nightmare of 167'060 bytes. This exceeds the maximum post length on SO, but I pasted it into a gist, so feel free to try it. Though you might want to run it by means other than manually pasting it into your console...

Low-level JS solution

This is alternative to this answer (I use matchAll idea from there in middle step). Main idea to generate character C (and more) using char code but without using quotes - it is possible when we define object field:

console.log(
  Function("return Object.entries({\\u0043:false})")()[0][0]
)

To transform this solution close to jsf I use following "helpers"

console.log(
  // "(" left parenthesis: 
  ([]["flat"]+"")[13], 
  
  // ")" right parenthesis:
  ([0]+false+[]["flat"])[20],
  
  // "{" left brace:
  (true+[]["flat"])[20],
  
  // "}" right brace:
  ([]["flat"]+"")["slice"]("-1"),
  
  // "+" plus
  (+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]])+[])[2],
  
  // "-" minus:
  (+((+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+[+[]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+!+[]]])+[])[!+[]+!+[]],
  
  // " " space:
  (NaN+[]["flat"])[11],
  
  // "." dot:
  (+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]],
  
  // "RegExp" string: (""+"".matchAll()).split(" ")[1]
  ([]+("")["matchAll"]())["split"](" ")[1],

  // ":" - colon: (Function("return RegExp")()()+"")[3]
  ([]["flat"]["constructor"]("return "+([]+("")["matchAll"]())["split"](" ")[1])()()+[])[3],

  // "/" - slash: (Function("return RegExp")()()+"")[0]
  ([]["flat"]["constructor"]("return "+([]+("")["matchAll"]())["split"](" ")[1])()()+[])[0], 
    
  // "\" - backslash: (Function("return RegExp(RegExp()+[])")()+[])[1]
  // (Function(("return "+false+"("+false+"()+[])").split(false).join("RegExp"))()+[])[1]
  ([]["flat"]["constructor"](("return "+false+"("+false+"()+[])")["split"](false)["join"](([]+("")["matchAll"]())["split"](" ")[1]))()+[])[1],
)

Finally we have (after full decoding it will have ~16k jsf characters)

// step 1
console.log(
  []["flat"]["constructor"]("return"+" "+"Object"+"."+"entries"+"("+"{"+"\\"+"u0043"+":"+false+"}"+")")()[0][0]
)

// step 2
console.log(
  []["flat"]["constructor"]("return"+" "+"Object"+"."+"entries"+([]["flat"]+"")[13]+(true+[]["flat"])[20]+([]["flat"]["constructor"](("return "+false+"("+false+"()+[])")["split"](false)["join"](([]+("")["matchAll"]())["split"](" ")[1]))()+[])[1]+"u0043"+":"+false+([]["flat"]+"")["slice"]("-1")+([0]+false+[]["flat"])[20])()[0][0]
)

Doing this we stay at level 3 according to Siguza answer

Here I develop Siguza answer if we use non deprecated de/encodeURI (level 5) we have

console.log(
  decodeURI(encodeURI(" ")[0]+"43"),
  (NaN+[]["entries"]()["to"+String["name"]]["call"]())[11] // here is for U came from
)

The entire approach depends on escape("<"), which yields "%3C". We can easily substitute that with escape(","), which gives us "%2C". JSFuck already has a way to obtain ",": [[]]["concat"]([[]])+[]

escape(",")[2]

becomes

Function("return escape")()(",")[2]

becomes

[]["flat"]["constructor"]("return escape")()([[]]["concat"]([[]])+[])[2]

becomes

[]["f"+"l"+"a"+"t"]["c"+"o"+"n"+"s"+"t"+"r"+"u"+"c"+"t"+"o"+"r"]("r"+"e"+"t"+"u"+"r"+"n"+" "+"e"+"s"+"c"+"a"+"p"+"e")()([[]]["c"+"o"+"n"+"c"+"a"+"t"]([[]])+[])[2]

becomes

[][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]][([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+[][[]])[+!+[]]+([]+![])[!+[]+!+[]+!+[]]+([]+!![])[+[]]+([]+!![])[+!+[]]+([]+[][[]])[+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([]+!![])[+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+!![])[+!+[]]](([]+!![])[+!+[]]+([]+!![])[!+[]+!+[]+!+[]]+([]+!![])[+[]]+([]+[][[]])[+[]]+([]+!![])[+!+[]]+([]+[][[]])[+!+[]]+([]+[][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]])[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+!![])[!+[]+!+[]+!+[]]+([]+![])[!+[]+!+[]+!+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([]+![])[+!+[]]+(+([]+(+!+[]+!+[])+(+!+[])+(+!+[])))[([]+!+[])[+[]]+([]+[][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]])[+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+([]+[])[([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+[][[]])[+!+[]]+([]+![])[!+[]+!+[]+!+[]]+([]+!![])[+[]]+([]+!![])[+!+[]]+([]+[][[]])[+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([]+!![])[+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+!![])[+!+[]]])[+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+!+[])[+[]]+([]+!+[])[+!+[]]+([]+[][[]])[+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+[][[]])[+!+[]]+([]+([]+[])[([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+[][[]])[+!+[]]+([]+![])[!+[]+!+[]+!+[]]+([]+!![])[+[]]+([]+!![])[+!+[]]+([]+[][[]])[+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([]+!![])[+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+!![])[+!+[]]])[[]+(+!+[])+(+!+[]+!+[]+!+[]+!+[])]]([]+(+!+[]+!+[]+!+[])+(+!+[]))[+!+[]]+([]+!![])[!+[]+!+[]+!+[]])()([[]][([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+[][[]])[+!+[]]+([][([]+![])[+[]]+([]+![])[!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]+[])[!+[]+!+[]+!+[]]+([]+![])[+!+[]]+([]+!![])[+[]]]([[]])+[])[!+[]+!+[]]

I did this manually though, so it may not be the optimal/shortest representation.

This is development of my previous answer - here is the TRICK which allows to get string RegExp (but we use letter R here) without throwing any exception (!) - (so we are in level 4 according to Siguza noation)

// TRICK:
console.log(
  (""+"".matchAll(""))   // 1. this gives "...RegExp..."
)


// encode/decode URI strings
console.log(
  "encode"+(NaN+[]["entries"]()["to"+String["name"]]["call"]())[11]+(NaN+""["matchAll"](""))[11]+([]+(+"1e1000"))[0],
  
  "decode"+(NaN+[]["entries"]()["to"+String["name"]]["call"]())[11]+(NaN+""["matchAll"](""))[11]+([]+(+"1e1000"))[0]
)



// Final formula
console.log(
  decodeURI(encodeURI(" ")[0]+"43")
)

And expnded final formula

// JSF (with allowed strings and numbers and NaN)
console.log(
  []["flat"]["constructor"]("return "+"decode"+(NaN+[]["entries"]()["to"+([]+[])["constructor"]["name"]]["call"]())[11]+(NaN+""["matchAll"](""))[11]+([]+(+"1e1000"))[0])()([]["flat"]["constructor"]("return "+"encode"+(NaN+[]["entries"]()["to"+([]+[])["constructor"]["name"]]["call"]())[11]+(NaN+""["matchAll"](""))[11]+([]+(+"1e1000"))[0])()((NaN+[]["flat"])[11])[0]+43)
)

This approach uses Base64 encoding to get the letter C. Keep note that C in Base64 represents the bits 000010.

Let's look at a run of 3 bytes that are being encoded in base64. We're going to call these 3 bytes data.

data = [aaaaaabb bbbbcccc ccdddddd]
              ^-----^

We're going to focus on the 2nd Base64 character in this run. To get it to be C, the low 2 bits of data[0] must be 00 and the high 4 bits of data[1] must be 0010. We can achieve this using the string '0 ':

'0' -> 0b00110000
' ' -> 0b00100000

data = [00110000 00100000 xxxxxxxx]
              ^-----^

Note: I chose "0 " because it was achievable with the least amount of characters. There are plenty of other pairs of characters that could achieve a similar result.

And voilĂ ! There's our solution. Now, we simply have to JSF*ck-ify this:

btoa("0 ")[1]
// Replace btoa with an arbitrary function
Function("return btoa")()("0 ")[1]

Here's how that is rendered in its full glory:

const capital_c = ([])[(![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][(([])[(![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+!+[]+!+[]]+(([])[(!![]+[])[+!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[(+!![])+[+[]]]+(!![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]]]()+[])[+!+[]]+([][[]]+[])[+!+[]]+(![]+[])[+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+(([])[(![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]]+(([])[(![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(([])[(!![]+[])[+!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[(+!![])+[+[]]]+(!![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]]]()+[])[+!+[]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[+!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(([])[(![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+([+[![]]]+([])[(![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!![]+[+!+[]]]+(([])[(!![]+[])[+!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[(+!![])+[+[]]]+(!![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]]]()+[])[+!+[]+!+[]]+(!![]+[])[+[]]+(([])[(!![]+[])[+!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[(+!![])+[+[]]]+(!![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]]]()+[])[+!+[]]+(![]+[])[+!+[]])()([+[]]+([+[![]]]+([])[(![]+[])[+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!![]+[+!+[]]])[+!+[]];
console.log(capital_c);

Total length: 1606 bytes

Related