9

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)

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345

6 Answers6

16

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...

Siguza
  • 21,155
  • 6
  • 52
  • 89
  • Thank you for your deep and "full" answer :) I hope that in future ESx JS standards they introduce something which will allow to get 'C' in easy way – Kamil Kiełczewski Sep 02 '20 at 22:13
  • 1
    If this is interesting for you, recently I develop tool which allow to create small js-fuck codes - look [here](https://github.com/kamil-kielczewski/small-jsfuck) (however currently it based on deprecated methods - but I work on it) – Kamil Kiełczewski Sep 02 '20 at 22:16
  • When I run that looong one from link in chrome console I get `undefined` - :P – Kamil Kiełczewski Sep 02 '20 at 22:39
  • @KamilKiełczewski What environment? I tested on Firefox, Chrome and NodeJS... – Siguza Sep 02 '20 at 22:40
  • I paste it to chrome console. I also see that we can a little short you solution decode/encodeURI are not deprecated so we can write: `decodeURI(encodeURI(" ")[0]+"43")` where we get U from `(NaN+[]["entries"]()["to"+String["name"]]["call"]())[11]` If I not miss anything – Kamil Kiełczewski Sep 02 '20 at 22:44
  • 1
    @KamilKiełczewski Oh wow, neat! I was trying to scrape together `U`, `R` and `I`, but couldn't find anywhere to get the `U`. Yes, that's totally possible then. Also, I think the reason you're getting `undefined` is because the CSP for `gist.githubusercontent.com` disallows `Function()` constructor calls. In Firefox I get a diagnostic message about it, but Chrome just silently fails. If you paste it into the console on SO though, it should work. – Siguza Sep 02 '20 at 22:59
  • 1
    Yes you are right - when I switch to new tab it works and return 'C' - Im surprised that pages can impose restrictions to chrome console (I don't know this). Thank you very much again for you answer - don't short it because maybe in future decodeURI will be also deprecated... :P – Kamil Kiełczewski Sep 02 '20 at 23:04
  • I discover new [trick here](https://stackoverflow.com/a/63802899/860099) - may be you will be able to develop it (in another answer)? – Kamil Kiełczewski Sep 08 '20 at 23:43
  • @KamilKiełczewski nice, but I don't think it merits a new answer. I mean, you can just replace my levels 5 & 6 with that. – Siguza Sep 08 '20 at 23:58
  • it is actually non trivial trick which allows to avoid throwing any exception - so I think it is major improvement, worth to be in separate answer – Kamil Kiełczewski Sep 08 '20 at 23:59
  • after long meditation - I found even better [solution](https://stackoverflow.com/a/63850312/860099) – Kamil Kiełczewski Sep 11 '20 at 15:52
5

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

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
3

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
)
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
2

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.

Siguza
  • 21,155
  • 6
  • 52
  • 89
  • 1
    Escape is deprecated though, isn't it? – ObsoleteAwareProduce Aug 31 '20 at 17:08
  • It is not deprecated [escape](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/escape) – Carlos1232 Aug 31 '20 at 17:11
  • 1
    @Siguza - Great answer - you save jsfuck :) - I will accept this answer - however escape is "partially" deprecated - so if someone in the future give solution which will be fully non-deprecated then I will switch and accept that person answer. Thank you :). – Kamil Kiełczewski Aug 31 '20 at 17:28
1

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)
)
Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
1

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

itzjackyscode
  • 970
  • 9
  • 27
  • your solution based on `btoa("0 ")` is longer than solution based on `btoa("t.")` given in this [question](https://stackoverflow.com/q/63673610/860099) few years ago – Kamil Kiełczewski Jul 13 '22 at 21:42