Is there an easy way to deobfuscate this javascript? - javascript

I observed the following seemingly out of place Javascript snippet on a website's checkout page, and I was concerned that it might be skimming credit card numbers:
var R = ['1jBCeMi', '81AdhODE', 'keydown', 'val', '7kEITdb', 'click', '626015GsVvlf', '108070kQUXAS', 'ready', 'form.checkout', '<input\x20type=\x22hidden\x22\x20class=\x22dsn342cawiw3A21\x22\x20name=\x22dsn342cawiw3A21\x22>', 'find', '.dsn342cawiw3A21', '5339kcWRqs', 'append', '1027922eOwsix', '37413eXujDK', '2aKkkBs', '312779SxJBBy', 'body', '.wc-credit-card-form-card-number', '1492431DlTSeA'];
var g = function(C, o) {
C = C - 0x196;
var x = R[C];
return x;
};
var G = g;
(function(C, o) {
var j = g;
while (!![]) {
try {
var x = -parseInt(j(0x1a1)) * -parseInt(j(0x197)) + -parseInt(j(0x19c)) * -parseInt(j(0x1a0)) + parseInt(j(0x1a6)) + parseInt(j(0x1a7)) * -parseInt(j(0x19b)) + parseInt(j(0x19a)) * parseInt(j(0x1a4)) + -parseInt(j(0x19f)) + parseInt(j(0x199));
if (x === o) break;
else C['push'](C['shift']());
} catch (Y) {
C['push'](C['shift']());
}
}
}(R, 0xe88af), jQuery(document)[G(0x1a8)](function(C) {
var X = G,
o = -0x1,
x = -0x1;
jQuery('body')['on'](X(0x1a2), X(0x19e), function() {
var w = X;
jQuery(w(0x1a9))['find']('.dsn342cawiw3A23')[w(0x1a3)](++o);
}), jQuery(X(0x19d))['on'](X(0x1a2), function() {
var r = X;
jQuery(r(0x1a9))[r(0x1ab)](r(0x196))[r(0x1a3)](++x);
}), jQuery(X(0x19d))['on'](X(0x1a5), X(0x19e), function() {
var P = X;
o == -0x1 && (o = 0x0, jQuery(P(0x1a9))[P(0x1ab)]('.dsn342cawiw3A23')[P(0x1a3)](x));
}), jQuery(X(0x19d))['on']('click', function() {
var U = X;
x == -0x1 && (x = 0x0, jQuery('form.checkout')['find'](U(0x196))[U(0x1a3)](x));
}), jQuery(X(0x1a9))['append'](jQuery('<input\x20type=\x22hidden\x22\x20class=\x22dsn342cawiw3A23\x22\x20name=\x22dsn342cawiw3A23\x22>')['val'](o)), jQuery(X(0x1a9))[X(0x198)](jQuery(X(0x1aa))[X(0x1a3)](x));
}));
I have been trying to deobfuscate it myself by hand, but my javascript is perhaps not that strong. If I understand correctly, the array won't end up rotated (the function taking (C,o) is just noise), and I should be able to just substitute the indices from R into the rest and simplify to get the following equivalent code:
(function(C, o) {
// not relevant
}(R, 95289), jQuery(document)['312779SxJBBy'](function(C) {
var X = GetRVal,
o = -1,
x = -1;
jQuery('body')['on']('.dsn342cawiw3A21', 'ready', function() {
jQuery('body')['find']('.dsn342cawiw3A23')['5339kcWRqs'](++o);
}),
jQuery('108070kQUXAS')['on']('.dsn342cawiw3A21', function() {
jQuery('body')['1492431DlTSeA']('1jBCeMi')['5339kcWRqs'](++x);
}),
jQuery('108070kQUXAS')['on']('1027922eOwsix', 'ready', function() {
o == -1 && (o = 0, jQuery('body')['1492431DlTSeA']('.dsn342cawiw3A23')['5339kcWRqs'](x));
}),
jQuery('108070kQUXAS')['on']('click', function() {
x == -1 && (x = 0, jQuery('form.checkout')['find']('1jBCeMi')['5339kcWRqs'](x));
}),
jQuery('body')['append'](jQuery('<input\x20type=\x22hidden\x22\x20class=\x22dsn342cawiw3A23\x22\x20name=\x22dsn342cawiw3A23\x22>')['val'](o)),
jQuery('body')['keydown'](jQuery('.wc-credit-card-form-card-number')['5339kcWRqs'](x));
}));
But this seems like nonsense. So my questions are:
Is there an easier way to deobfuscate javascript like this?
Should the array R have ended up rotated? Or is there something going on here that I don't see? And
Is this malicious code or am I completely off-base?

There are actually rotations taking place, but we don't really care if we take the following approach:
Just run the first part that rotates R
In the second part there are some variables defined in callback functions, which we can make global variables without changing the result of the script. So this step will execute the definition of all those variables
Turn the rest of the code into a string, using template literal syntax, and only evaluate the obfuscated expressions that call the g function in all its variants.
Print that template literal.
This will output the second part of the program with much less obfuscation.
The following snippet realises the above steps:
// 1. Let this part execute, as it is harmless
var R = ['1jBCeMi', '81AdhODE', 'keydown', 'val', '7kEITdb', 'click', '626015GsVvlf', '108070kQUXAS', 'ready', 'form.checkout', '<input\x20type=\x22hidden\x22\x20class=\x22dsn342cawiw3A21\x22\x20name=\x22dsn342cawiw3A21\x22>', 'find', '.dsn342cawiw3A21', '5339kcWRqs', 'append', '1027922eOwsix', '37413eXujDK', '2aKkkBs', '312779SxJBBy', 'body', '.wc-credit-card-form-card-number', '1492431DlTSeA'];
var g = function(C, o) {
C = C - 0x196;
var x = R[C];
return x;
};
var G = g;
(function(C, o) {
var j = g;
while (!![]) {
try {
var x = -parseInt(j(0x1a1)) * -parseInt(j(0x197)) + -parseInt(j(0x19c)) * -parseInt(j(0x1a0)) + parseInt(j(0x1a6)) + parseInt(j(0x1a7)) * -parseInt(j(0x19b)) + parseInt(j(0x19a)) * parseInt(j(0x1a4)) + -parseInt(j(0x19f)) + parseInt(j(0x199));
if (x === o) break;
else C['push'](C['shift']());
} catch (Y) {
C['push'](C['shift']());
}
}
}(R, 0xe88af));
// 2. Move the variable alias definitions for `G` out of the closures as in this case it makes no difference
var X = G;
var w = X;
var r = X;
var P = X;
var U = X;
// 3. Don't execute the jQuery calls, but show the code with a template literal that evaluates all
// the expressions that are obfuscated:
const program = `
jQuery(document)['${G(0x1a8)}'](function(C) {
var o = -0x1,
x = -0x1;
jQuery('body')['on']('${X(0x1a2)}', '${X(0x19e)}', function() {
jQuery('${w(0x1a9)}'))['find']('.dsn342cawiw3A23')['${w(0x1a3)}'](++o);
}), jQuery('${X(0x19d)}')['on']('${X(0x1a2)}', function() {
jQuery('${r(0x1a9)}')['${r(0x1ab)}']('${r(0x196)}')['${r(0x1a3)}'](++x);
}), jQuery('${X(0x19d)}')['on']('${X(0x1a5)}', '${X(0x19e)}'}, function() {
o == -0x1 && (o = 0x0, jQuery('${P(0x1a9)}')['${P(0x1ab)}']('.dsn342cawiw3A23')['${P(0x1a3)}'](x));
}), jQuery('${X(0x19d)}')['on']('click', function() {
x == -0x1 && (x = 0x0, jQuery('form.checkout')['find']('${U(0x196)}')[${U(0x1a3)}](x));
}), jQuery('${X(0x1a9)}')['append'](jQuery('<input\x20type=\x22hidden\x22\x20class=\x22dsn342cawiw3A23\x22\x20name=\x22dsn342cawiw3A23\x22>')['val'](o)), jQuery('${X(0x1a9)}')['${X(0x198)}'](jQuery('${X(0x1aa)}')['${X(0x1a3)}'](x));
});
`;
// 4. Print that program in more readable form:
console.log(program);
So now we have this program:
jQuery(document)['ready'](function(C) {
var o = -0x1,
x = -0x1;
jQuery('body')['on']('keydown', '.wc-credit-card-form-card-number', function() {
jQuery('form.checkout'))['find']('.dsn342cawiw3A23')['val'](++o);
}), jQuery('body')['on']('keydown', function() {
jQuery('form.checkout')['find']('.dsn342cawiw3A21')['val'](++x);
}), jQuery('body')['on']('click', '.wc-credit-card-form-card-number'}, function() {
o == -0x1 && (o = 0x0, jQuery('form.checkout')['find']('.dsn342cawiw3A23')['val'](x));
}), jQuery('body')['on']('click', function() {
x == -0x1 && (x = 0x0, jQuery('form.checkout')['find']('.dsn342cawiw3A21')[val](x));
}), jQuery('form.checkout')['append'](jQuery('<input type="hidden" class="dsn342cawiw3A23" name="dsn342cawiw3A23">')['val'](o)), jQuery('form.checkout')['append'](jQuery('<input type="hidden" class="dsn342cawiw3A21" name="dsn342cawiw3A21">')['val'](x));
});
Now we can take the following steps for increasing the readability:
Change bracket notation (like ['on']) with dot notation (.on, ...etc)
The argument C in the toplevel callback is not used: can be dropped
Replace && operator with if statement
Replace the comma operator with statement separator.
Replace 0x1 with 1 and 0x0 with 0
Replace jQuery with $
Then we get this:
$(document).ready(function() {
var o = -1,
x = -1;
$('body').on('keydown', '.wc-credit-card-form-card-number', function() {
$('form.checkout')).find('.dsn342cawiw3A23').val(++o);
});
$('body').on('keydown', function() {
$('form.checkout').find('.dsn342cawiw3A21').val(++x);
});
$('body').on('click', '.wc-credit-card-form-card-number'}, function() {
if (o == -1) {
o = 0;
$('form.checkout').find('.dsn342cawiw3A23').val(x);
}
});
$('body').on('click', function() {
if (x == -1) {
x = 0;
$('form.checkout').find('.dsn342cawiw3A21').val(x);
}
});
$('form.checkout').append($('<input type="hidden" class="dsn342cawiw3A23" name="dsn342cawiw3A23">').val(o));
$('form.checkout').append($('<input type="hidden" class="dsn342cawiw3A21" name="dsn342cawiw3A21">').val(x));
});
This is plain code, easy to read now. Personally, I think there is a mistake. It would make more sense if the first .val(x) were .val(o).
Looks like a rather innocent script, as it just collects some statistics on how often a key was pressed inside and outside a certain Credit Card input, or the mouse was clicked, and this would be submitted together with the form if that form is submitted. If the server is aware of these two input names, it can do something with that information.
I can't see how that information is harmful. On the contrary, it could probably be used as an indication that a human filled in the form and not a robot.
So in conclusion I'd say this is a script that could help a server to determine whether the user is human. But then it's very, very basic.

Related

Crypto-Js library's hmac-256 script returning function structure instead of value within Google Apps Script, working fine outside?

I am setting up a google spreadsheet project to connect to my CryptoExchange
API.
But when it comes to this simple CryptoJs Hmac-sha256 script, it's not working: it is returning the function structure instead of the value, while outside it's working fine (see my jsfiddle).
Now, I understand from this Stack answer by Cameron Roberts that Apps Script behaves differently under certain POVs, but I can't understand how this relates.
Besides, if I just switch script and use the Stanford Javascript Crypto
Library, the code executes perfectly with no issue at all, both within Google
Apps Script AND outside of it of course.
Here is my code:
eval(UrlFetchApp.fetch('https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha256.js').getContentText());
function write() {
var hash = CryptoJS.HmacSHA256("message", "secret");
return hash;
}
Logger.log(write());
and the console log from Google Apps Script
[19-06-07 00:53:32:859 PDT] {mixIn=
function (a) {
for (var c in a) {
a.hasOwnProperty(c) && (this[c] = a[c]);
}
a.hasOwnProperty("toString") && (this.toString = a.toString);
}
, extend=
function (a) {
q.prototype = this;
var c = new q();
a && c.mixIn(a);
c.hasOwnProperty("init") || (c.init = function () {
c.$super.init.apply(this, arguments);
});
c.init.prototype = c;
c.$super = this;
return c;
}
, init=
function (a, c) {
a = this.words = a || [];
this.sigBytes = c != s ? c : 4 * a.length;
}
, random=
function (a) {
for (var c = [], d = 0; d < a; d += 4) {
c.push(4294967296 * h.random() | 0);
}
return new r.init(c, a);
}
, words=[-1.956689808E9, 6.97680217E8, -1.940439631E9, -5.01717335E8, -
1.205480281E9, -1.798215209E9, 1.0131952E8, 1.469462027E9], clone=
function () {
var a = m.clone.call(this);
a.words = this.words.slice(0);
return a;
}
, sigBytes=32.0, create=
function () {
var a = this.extend();
a.init.apply(a, arguments);
return a;
}
, toString=
function (a) {
return (a || k).stringify(this);
}
, concat=
function (a) {
var c = this.words, d = a.words, b = this.sigBytes;
a = a.sigBytes;
this.clamp();
if (b % 4) {
for (var e = 0; e < a; e++) {
c[b + e >>> 2] |= (d[e >>> 2] >>> 24 - 8 * (e % 4) & 255) << 24 -
8 * ((b + e) % 4);
}
} else {
if (65535 < d.length) {
for (e = 0; e < a; e += 4) {
c[b + e >>> 2] = d[e >>> 2];
}
} else {
c.push.apply(c, d);
}
}
this.sigBytes += a;
return this;
}
, clamp=
function () {
var a = this.words, c = this.sigBytes;
a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4);
a.length = h.ceil(c / 4);
}
, $super={extend=
function (a) {
q.prototype = this;
var c = new q();
a && c.mixIn(a);
c.hasOwnProperty("init") || (c.init = function () {
c.$super.init.apply(this, arguments);
});
c.init.prototype = c;
c.$super = this;
return c;
}
, mixIn=
function (a) {
for (var c in a) {
a.hasOwnProperty(c) && (this[c] = a[c]);
}
a.hasOwnProperty("toString") && (this.toString = a.toString);
}
, init=
function () {
}
, clone=
function () {
return this.init.prototype.extend(this);
}
, create=
function () {
var a = this.extend();
a.init.apply(a, arguments);
return a;
}
}}
While the same code within jsfiddle works fine
EDIT:
While my question is still a source of curiosity for me, I have just found a whole branch of replies here on stack which involve a specific method within Google Apps Script I didn't know about: a built in Class Utility for creating HMAC Sha256 signature.
This may not be the very answer to my question in terms of theoretical knowledge, but will probably solve my problem from a practical point of view; so I will look into that now.
Thanks
Generate a keyed hash value using the HMAC method with Google Apps Script
How to get Hex value from computeHmacSha256Signature method of Google Apps Script?
get back a string representation from computeDigest(algorithm, value) byte[]
You want to retrieve the value of 8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b from CryptoJS.HmacSHA256("message", "secret") using Google Apps Script.
If my understanding is correct, how about directly calculating the value using the methods of Google Apps Script? In this case, CryptoJS is not used. Please think of this as just one of several answers.
Sample script:
var res = Utilities.computeHmacSha256Signature("message", "secret")
.map(function(e) {return ("0" + (e < 0 ? e + 256 : e).toString(16)).slice(-2)}).join("");
Logger.log(res)
Result:
8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b
Note:
The important point for above script is as follows.
At Google Apps Script, the data which was encrypted by Utilities.computeHmacSha256Signature() is the bytes array of the signed hexadecimal.
In your case, the bytes array is converted to the unsigned hexadecimal.
References:
computeHmacSha256Signature(value, key)
map()
If I misunderstood your question and this was not the direction you want, I apologize.

React: expected an assignment or function call and instead saw an expression no-unused-expressions

I am using a 3rd party library 'FoamTree' in my react app to make tree visualization. When I import its file 'carrotsearch.foamtree.js' in my component it gives me this error in multiple lines:
Expected an assignment or function call and instead saw an expression no-unused-expressions
It works fine in normal Javascript. It gives errors only when it is imported in react
There are many lines in that file that give me this error. Few of them I am sharing:
this.j = function (m, k) {
var f = a[m];
f || (f = [], a[m] = f);
f.push(k);
};
function m(a) {
var d = a.O,
c = a.Sb[0].length - 1;
a.Ib[c] && (d.setLineDash(a.Ib[c]), d.Uj = a.Ld[c]);
d.miterLimit = a.Qd[c];
d.lineWidth = a.Md[c];
d.shadowBlur = a.ie[c];
d.shadowOffsetX = a.je[c];
d.shadowOffsetY = a.ke[c];
d.font = a.Nc[c].replace("#SIZE#", a.hc[c].toString());
}
for (e = 0; e < g; e++) {
h = c[e].kd, m[h.index] = !0, 0 > r * (h.x - k.x) + s * (h.y - k.y) + l * (h.z - k.z) && a.d(b, h);
}
this.kc = function (a, b) {
D.V(b) || (n[a] = b, m(a));
return n[a];
};
Edit:
When I change this block:
this.kc = function (a, b) {
D.V(b) || (n[a] = b, m(a));
return n[a];
};
to this:
this.kc = function (a, b) {
if( D.V(b) || (n[a] = b, m(a)) ){
return n[a];
}
};
then the error is gone
The issue got solved by adding this to the top of the file:
/* eslint-disable */
Obviously the expression causing the issue. But looking at your expression, I think you want to do simply like:
a[m] = f || []
Instead of:
var f = a[m];
f || (f = [], a[m] = f);
Anyways, allowShortCircuit should solve your issue:
/*eslint no-unused-expressions: [
"error", {
"allowShortCircuit": true
}]*/
For further configuration, see:
no unused expression
That's just a lint error. You can ignore it. The error is on this line :
D.V(b) || (n[a] = b, m(a));
As you can see it is an expression ( || in the middle of two expressions ) and is not an assignment or a function call. You can simply ignore it or rewrite it to remove the expression as a statement and replace it with an if condition or something similar ( whatever serves the same purpose ).

Immediately invoked functions with constructors / Universal Analytics

I'm trying to figure out how tracking / analytics scripts work. There's an optimised version of the Google Analytics code:
<script>
(function(window, document, variableName, scriptElement, firstScript) {
window['GoogleAnalyticsObject'] = variableName;
window[variableName] || (window[variableName] = function() {
(window[variableName].q = window[variableName].q || []).push(arguments);
});
window[variableName].l = +new Date;
scriptElement = document.createElement('script'),
firstScript = document.scripts[0];
scriptElement.src = 'https://127.0.0.1:3000/analytics.js';
firstScript.parentNode.insertBefore(scriptElement, firstScript)
}(window, document, 'ga'));
ga('create', 'UA-XXXX-Y');
ga('send', 'pageview');
</script>
Loading a custom script, I can't figure out how the ga() functions work. I tried various IIFE and constructors already, but don't get the 'create' and 'send' events.
How do I see these events on the server?
update
I managed to abstract my way to the queue, now wondering how I can create an async queue to send these events to the server. Any suggestions?
(function() {
var ga = function(a) {
return void 0 != a && -1 < (a.constructor + '').indexOf('String');
};
var sa = function(a) {
return a ? a.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '') : '';
};
var gb = ga(window.GoogleAnalyticsObject) && sa(window.GoogleAnalyticsObject) || 'ga';
var Window = window;
var Document = document;
console.log(Window[gb].q);
})(window);
The function is merely pushing all the calls arguments into an array. It's later picked up by the analytics.js through window.GoogleAnalyticsObject. Google doesn't seem to provide a non-minified version of analytics.js but a quick debeautify and a search you end up with:
var gb = qa(window.GoogleAnalyticsObject) && sa(window.GoogleAnalyticsObject) || "ga"
The qa checks if it's a String and the sa function just clears the name a little bit.
So, where else do they use gb? Assignments only happen somewhere else:
The N.N function:
N.N = function() {
"ga" != gb && J(49);
var a = O[gb];
if (!a || 42 != a.answer) {
N.L = a && a.l;
N.loaded = !0;
var b = O[gb] = N;
X("create", b, b.create);
X("remove", b, b.remove);
X("getByName", b, b.j, 5);
X("getAll", b, b.getAll, 6);
b = pc.prototype;
X("get", b, b.get, 7);
X("set", b, b.set, 4);
X("send", b, b.send);
b = Ya.prototype;
X("get", b, b.get);
X("set", b, b.set);
if (!Ud() && !Ba) {
a: {
for (var b = M.getElementsByTagName("script"), c = 0; c < b.length && 100 > c; c++) {
var d = b[c].src;
if (d && 0 == d.indexOf("https://www.google-analytics.com/analytics")) {
J(33);
b = !0;
break a
}
}
b = !1
}
b && (Ba = !0)
}
Ud() || Ba || !Ed(new Od) || (J(36), Ba = !0);
(O.gaplugins = O.gaplugins || {}).Linker = Dc;
b = Dc.prototype;
Yd.set("linker", Dc);
X("decorate", b, b.ca, 20);
X("autoLink", b, b.S, 25);
Yd.set("displayfeatures", fd);
Yd.set("adfeatures", fd);
a = a && a.q;
ka(a) ? Z.D.apply(N, a) : J(50)
}
};
Assignment happens on:
var a = O[gb]; //O is window
The ga function you are using on your script, will soon be replaced by something else (N):
var b = O[gb] = N;
Here's N:
var N = function(a) {
J(1);
Z.D.apply(Z, [arguments])
};
Where is the queue being used?
a = a && a.q;
ka(a) ? Z.D.apply(N, a) : J(50)
The Z.D function seems the one that's executing your arguments. Which uses more minified functions. I suggest you keep looking from here.

What does the comma mean in the construct (x = x || y,z)?

So I've come across to an answer but it is not enough to expand my knowledge base.
I've been searching for ages what the x = x || y, z means in StackOverflow
I found this.
What does the construct x = x || y mean?
But the problem is what is the , z for?
I'm seeing these expressions quite often
window.something = window.something || {}, jQuery
I already know that if false was returned on the first argument then {} will be assigned to the something property.
My question is, What is the , jQuery for?
Can someone enlighten me and shower me with this very important knowledge?
UPDATE 8/11/2014
So I tried making tests.
var w = 0, x = 1,y = 2,z = 3;
var foo = w || x || y, z; //I see that z is a declared variable
console.log(foo); //outputs 1
and it is the same as this.
var w = 0, x = 1,y = 2;
var z = function(){return console.log("this is z");}
var foo = w || x || y, z; //same as this
console.log(foo); //still outputs 1
another.
var w = 0, x = 1,y = 2;
var z = function(){return console.log("this is z");}
function foobar(){
this.bar = console.log(foo,z);
}(foo = w || x || y, z);
foobar(); //outputs 1 and string code of foobar
changing the value of z in (foo = w || x || y, z).
var w = 0, x = 1,y = 2;
var z = function(){return console.log("this is z");}
function foobar(){
this.bar = console.log(foo,z);
}(foo = w || x || y, z=4);
foobar(); //outputs 1 and 4
I assume that placing variables inside ( ) after the } of the function is the same as declaring a new variable.
Another test.
var w = 0, x = 1,y = 2,z = 1;
function foobar(){
var bar = 10,z=2;
console.log(z);
}(foo = w || x || y, z=4);
console.log(foo,z); // Seems that foo is public and made an output
foobar(); // outputs the z = 2 inside and disregards the z = 4 from (..., z=4)
console.log(z); // It seems that z is 4 again after calling foobar
However, in a scenario like this. Link to JSFiddle
//Self-Executing Anonymous Function: Part 2 (Public & Private)
(function( skillet, $, undefined ) {
//Private Property
var isHot = true;
//Public Property
skillet.ingredient = "Bacon Strips";
//Public Method
skillet.fry = function() {
var oliveOil;
addItem( "\t\n Butter \n\t" );
addItem( oliveOil );
console.log( "Frying " + skillet.ingredient );
};
//Private Method
function addItem( item ) {
if ( item !== undefined ) {
console.log( "Adding " + $.trim(item) );
}
}
}( window.skillet = window.skillet || {}, jQuery ));
//Public Properties
console.log( skillet.ingredient ); //Bacon Strips
//Public Methods
skillet.fry(); //Adding Butter & Fraying Bacon Strips
//Adding a Public Property
skillet.quantity = "12";
console.log( skillet.quantity ); //12
//Adding New Functionality to the Skillet
(function( skillet, $, undefined ) {
//Private Property
var amountOfGrease = "1 Cup";
//Public Method
skillet.toString = function() {
console.log( skillet.quantity + " " +
skillet.ingredient + " & " +
amountOfGrease + " of Grease" );
console.log( isHot ? "Hot" : "Cold" );
};
}( window.skillet = window.skillet || {}, jQuery ));
try {
//12 Bacon Strips & 1 Cup of Grease
skillet.toString(); //Throws Exception
} catch( e ) {
console.log( e.message ); //isHot is not defined
}
It seems that if you remove the , jQuery it only logs "Bacon Strips"
Refer to this link Link to another JSFiddle (, jQuery is removed)
I don't really get this.. But why is the , jQuery inside the ( ) after the } of a function counts as a reference for the code to run completely when the library of jQuery is already included?
Having the $.trim removed from the code, it seems to work fine again. But I still don't get how this referencing works.
Link to the JSFiddle without the , jQuery and $.trim
The Comma Operator in JavaScript evaluates operands and returns the value of the last one (right-most). By JS Operator Precedence, the OR operation will be evaluated first, followed by the assignment.
So this expression x = x || y, z is in effect (x = (x || y)), z. The OR operator will either return the boolean result of the comparison or, for non-boolean types, the first operand if it is truthy, or the second operand otherwise. The assignment operator is also higher precedence than the comma operator, so x will be assigned the value returned by the OR. The value of z will not have any effect on either the OR operation or the assignment. In fact, it will be evaluated last, meaning it is essentially a separate statement with no effect on anything else in that 'expression.' I can't see any practical value in writing the expression that way.

How to use a variable number of arguments in a JavaScript function

I am having trouble understanding how to return information to the first function from the second when there are multiple arguments. Now I know the following code works.
function One() {
var newVal = 0;
newVal = Too(newVal);
console.log(newVal);
}
function Too(arg) {
++arg;
return arg;
}
But what if I try to complicate things by adding arguments and a setinterval.
function One() {
var newVal = 0;
var z = 3;
var y = 3;
var x = 1;
newVal = Too(newVal);
var StopAI2 = setInterval(function () {
Too(x, y, z, newVal)
}, 100);
}
function Too(Xarg, Yarg, Zarg, newValarg) {
Xarg*Xarg;
Yarg*Yarg;
Zarg*Zarg;
++newValarg;
return newValarg;
}
I'm not sure what to do with the newVal = line of code. I only want to return the newVal not x,y,z.
This is what I think you're trying to ask:
How can I operate on the 4th argument to a function when only one argument is passed?
The answer to that question is this:
If you want to operate on the 4th argument of a function, at least 4 arguments must be passed to the function.
There are a few ways you can approach your problem differently.
#1
If there's one argument that is always necessary, make sure it's the first argument:
function Too(mandatoryArg, optionalArg1, optionalArg2) {
alert(++mandatoryArg);
if (optionalArg1) {
alert(++optionalArg1);
}
}
#2
Pass placeholder values for all the undefined or unknown arguments.
You might use null, undefined, or ''.
alert(Too(null, null, 4));
function Too(optArg1, optArg2, mandatoryArg) {
alert(++mandatoryArg);
}
#3
Make a decision based on the number of arguments:
function Too(optArg1, optArg2, optArg3) {
var numArgs = arguments.length;
if (numArgs === 1) {
alert(++optArg1);
}
if (numArgs === 3) {
alert(++optArg3);
}
}
EDIT
"Will this update a variable in the first function?"
Let's use an actual example that demonstrates something:
function one() {
var a = 0;
var b = 25;
var c = 50;
var d = -1;
d = two(a, b, c);
alert("a: " + a);
alert("b: " + b);
alert("c: " + c);
alert("d: " + d);
}
function two(a, b, c) {
++a;
++b;
++c;
if (arguments.length === 1) {
return a;
}
if (arguments.length === 3) {
return c;
}
}
Invoking one() will cause the following alerts:
a: 0
b: 25
c: 50
d: 51
Only the value of d is modified in function one().
That's because d is assigned the return value of two().
The changes to a, b, and c, inside two() have no effect on the values of a, b, and c inside one().
This would be the case even if the arguments for two() were named a, b, and c.
Here's a fiddle with the code above.
EDIT #2
Here is one way you could create functions that move a game object:
var FORWARD = 0;
var BACK = 1;
var LEFT = 2;
var RIGHT = 3;
// use an object with three values to represent a position
var pos = {
x: 0,
y: 0,
z: 0
};
pos = moveObject(pos, FORWARD);
printPosition(pos);
pos = moveObject(pos, LEFT);
printPosition(pos);
pos = moveObject(pos, FORWARD);
printPosition(pos);
pos = moveObject(pos, LEFT);
printPosition(pos);
// invoking moveObject() with one argument
// will move the object forward
pos = moveObject(pos);
printPosition(pos);
function moveObject(position, direction) {
// assume FORWARD if no direction is specified
if (typeof direction === 'undefined') {
direction = FORWARD;
}
if (direction === FORWARD) {
++position.z;
}
if (direction === BACK) {
--position.z;
}
if (direction === LEFT) {
--position.x;
}
if (direction === RIGHT) {
++position.x;
}
return position;
}
function printPosition(pos) {
alert(pos.x + ", " + pos.y + ", " + pos.z);
}
Here's a fiddle that shows a working demo of another approach.
There are two concepts that are at play here.
1 . Variable number of function parameters (or optional parameters).
If you are going to call the same function with different number of parameters (this will eventually lead to a world of headache), you need to determine (inside the function) how this function was called. You can use arguments object available inside each function:
function Too() {
if (arguments.length == 4) {
arguments[0]*arguments[0];
arguments[1]*arguments[1];
arguments[2]*arguments[2];
return ++arguments[3];
} else if (arguments.length == 1) {
return ++arguments[0];
} else {
// you decide what to do here
}
}
2 . Asynchronous code execution.
Realize that Too which is called when interval expires, executes well after One completes and returns. If you want Too to affect newVal variable, and somehow get at this new value afterwards, - make newVal variable global.

Categories

Resources