I have a little chunk of code for transforming acronyms
var Acronyms = function () {};
Acronyms.prototype.parse = function(string) {
var array = string.split(' ');
var answer = '';
for (var i = 0; i < array.length; i++) {
answer += array[i][0];
}
return answer;
};
module.exports = Acronyms;
that when used with this test
describe('Acronyms are produced from', function(){
it('title cased phrases', function() {
expect(new Acronyms.parse('Portable Network Graphics')).toEqual('PNG');
});
});
Gives me: TypeError: Acronyms.parse is not a function
When I tried to search for as many keywords as I could I kept seeing things about semi colons so maybe the issue is there? I don't think it is but maybe I missed something.
new Acronyms.parse() attempts to get Acronyms.parse and use it as a constructor.
Instead, you want to use Acronyms as a constructor, and call the parse method of the instance. That can be achieved with
new Acronyms().parse() // preferred way
(new Acronyms).parse() // alternative
This is the reason it's not a good idea to omit the parentheses when instantiating a constructor.
You need to instantiate an Acronyms object using new Acronyms(), and then you can call parse(). As is, it's trying to use Acronyms.parse as the constructor, but that function doesn't exist (as it says) because parse is under the prototype.
Instead, use it like this:
new Acronyms().parse('Portable Network Graphics'))
Related
I've got a functional script on my site that allows me to open a link in a new window when a specific class is added to the link. I need a lot of those on my site though so I figured I'd make the script a bit easier to edit by working with variables.
In the process of changing out hardcoded strings for variables my script stopped working though. The only one that works is the var where I set the url.
I'm learning that ${} doesn't work everywhere. Hope that someone can point out where my thinking is wrong. Also hope that I got the terminology right, trying to learn though! :-)
var function1Name = "test_function";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class";
function ${function1Name}() {
window.open(function1Url, "_blank", "height=200");
}
jQuery("${function1Class}").click(function(){
${function1Name}()
});
None of your uses of ${} are valid JavaScript syntax.
Your function declaration van be replaced with:
window[function1Name] = function () {
window.open(function1Url, "_blank", "height=200");
}
Please note that the function will no longer be hoisted when declared this way, so order of operation matters.
The click handler can be written as follows:
jQuery(function1Class).click(function() { // Note that I just used the variable there.
window[function1Name]();
});
There is a ${} concept in JavaScript, but that is only in template literals:
const myVariable = "Foo";
const message = `myVariable contains: "${myVariable}"!`;
console.log(message);
There's several syntax issues here.
Firstly, function ${function1Name}() is not valid syntax. Function names must be defined before runtime. If you want to dynamically access a function, place it in an object and set the key with the variable reference.
Secondly, ${function1Name}() is again not valid syntax. You cannot invoke a function like that dynamically. Referring to the suggestion above, you can access an object dynamically so the first point fixes this problem.
Thirdly, string interpolation only works within template literals, so you need to delimit the string with backticks: ``. However it's completely redundant here as you can just use $(function1Class)
With those issues in mind, here's an updated example:
var function1Name = "test_function";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class";
var funcObj = {
[function1Name]: function() {
console.log(`function called, window would open here containing ${function1Url}...`);
// window.open(function1Url, "_blank", "height=200");
}
}
$(function1Class).click(function() {
funcObj[function1Name]()
});
/*
alternative using a template literal, although note that it's redundant here
$(`${function1Class}`).click(function() {
funcObj[function1Name]()
});
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Click me
One last thing to note is that no version of IE supports template literals, so be sure of your browser support requirements before using them.
So cool, I got it to work!
var function1Name = "test_function_1";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class1";
var function2Name = "test_function_2";
var function2Url = "https://www.cnn.com";
var function2Class = ".test_function_class2";
// Function 1
window[function1Name] = function () {
window.open(function1Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function1Class).click(function() {
window[function1Name]();
});
// Function 2
window[function2Name] = function () {
window.open(function2Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function2Class).click(function() {
window[function2Name]();
});
I can now add a bunch of url's and corresponding classes as was my intention. Super happy about that.
A follow up question though, as I'll have to experiment with what the ideal window parameters will be I'm trying to make those arguments variables as well. I've tried the examples of how to insert a variables output from the functional code but those methods don't work there. Here's that code:
var windowWidth = 250
var function1Name = "test_function_1";
var function1Url = "https://www.google.com";
var function1Class = ".test_function_class1";
var function2Name = "test_function_2";
var function2Url = "https://www.cnn.com";
var function2Class = ".test_function_class2";
// Function 1
window[function1Name] = function () {
window.open(function1Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=[windowWidth],height=745");
}
jQuery(function1Class).click(function() {
window[function1Name]();
});
// Function 2
window[function2Name] = function () {
window.open(function2Url, "_blank", "toolbar=no,status=no,scrollbars=yes,resizable=yes,top=500,left=500,width=600,height=745");
}
jQuery(function2Class).click(function() {
window[function2Name]();
});
How would I insert the variables value (2nd line of Function1) there ?
I'm trying to learn javascript. As part of that effort, I am writing a basic minimax AI. I have the following methods:
Computer.prototype.expand = function(node) {
/* adds all state action pairs to the node.successors array */
};
Computer.prototype.getMove = function(boardAr) {
console.log("getMove");
var b2 = boardAr.slice();
var i;
var action;
this.root = new TNode(b2, this.mark);
this.root.AIPlayedLast = false;
this.expand(this.root);
this.root.successors.forEach(this.minVal);
action = maxNode(root.successors);
this.draw(action);
registerMove(action, this.mark);
};
Computer.prototype.minVal = function(node) {
if (node.isTerminal) {
return;
} else {
this.expand(node);
node.successors.forEach(maxVal);
node.utility = this.minNode(node.successors).utility;
}
};
When the getMove method is called the subsequent call to expand goes as expected. But, when expand is called from the minVal method I get: Uncaught TypeError: undefined is not a function. I'm utterly perplexed by this. Any help/suggestions would be greatly appreciated.
I think the reason is in this row:
this.root.successors.forEach(this.minVal);
You pass minVal as contextless reference, it will not be called in a context of your Computer instance (this)
Here is how you can improve it:
var self = this;
this.root.successors.forEach(function() {
self.minVal.apply(self,arguments);
})
The simplest and quickest solution is just to change
this.root.successors.forEach(this.minVal);
to
this.root.successors.forEach(this.minVal.bind(this))
This solves the problem in the same as the other answers, but in a way some might consider more compact.
Or, you can pass a "this" to the forEach function as the second argument, a somewhat under-utilized feature of forEach:
this.root.successors.forEach(this.minVal, this)
This feature is also available on other Array prototype methods that take functions, including map, filter, some, every (but not reduce and reduceRight).
ES6 arrow functions handle this differently, so you can do
this.root.successors(forEach(e => this.minVal(e)));
The forEach() method might be called for each of the successors. So, you pass the Computer::minVal method (this.minVal), but with the TNode(?) as this-pointer. Try:
var that = this;
this.root.successors.forEach(function(node) {
that.minVal(node));
});
I am creating the AI engine for a JS game, and it's made of Finite State Machines. I am loading the number of states and their variable values from the XML. I also want to load the behaviour, and since I don't have the time to create a scripting language, I thought it would be a good idea to 'insert' JS code on external files (inside XML nodes), and execute it on demand.
Something like that
<evilguy1>
<behaviour>
this.x++;
</behaviour>
<behaviour>
this.y++;
</behaviour>
</evilguy1>
To something like that:
function behaviour_1(){
this.x++;
}
function behaviour_2(){
this.y++;
}
My question is, now that I have the code loaded, how can I execute it? I would like to create a function with an unique name for each code 'node', and then call them from the game logic, but I don't know if this is possible (Since you can load more JS code from the HTML, you should also be able to do it from the JS code, no?). If not, is there any similar solution? Thanks in advance!
(PS:The less external-library-dependent, the better)
Edit 1:
Ok, so now I know how to create functions to contain the code
window[classname] = function() { ... };
Well, you could use Function constructor, like in this example:
var f = new Function('name', 'return alert("hello, " + name + "!");');
f('erick');
This way you're defining a new function with arguments and body and assigning it to a variable f. You could use a hashset and store many functions:
var fs = [];
fs['f1'] = new Function('name', 'return alert("hello, " + name + "!");');
fs['f1']('erick');
Loading xml depends if it is running on browser or server.
To extend Ericks answer about the Function constructor.
The Function constructor creates an anonymous function, which on runtime error would print out anonymous for each function (created using Function) in the call stack. Which could make debugging harder.
By using a utility function you can dynamically name your created functions and bypass that dilemma. This example also merges all the bodies of each function inside the functions array into one before returning everything as one named function.
const _createFn = function(name, functions, strict=false) {
var cr = `\n`, a = [ 'return function ' + name + '(p) {' ];
for(var i=0, j=functions.length; i<j; i++) {
var str = functions[i].toString();
var s = str.indexOf(cr) + 1;
a.push(str.substr(s, str.lastIndexOf(cr) - s));
}
if(strict == true) {
a.splice(1, 0, '\"use strict\";' + cr)
}
return new Function(a.join(cr) + cr + '}')();
}
A heads up about the Function constructor:
A function defined by a function expression inherits the current
scope. That is, the function forms a closure. On the other hand, a
function defined by a Function constructor does not inherit any scope
other than the global scope (which all functions inherit).
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Differences
Assuming you have an array of node names and a parallel array of function body's:
var functions = {};
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviorsNames.length; i++){
functions[behaviorsNames[i]] = new Function(behaviorsBodies[i]);
}
//run a function
functions.behavior1();
or as globals:
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviors.length; i++){
window[behaviors[i]] = new Function(behaviorsBodies[i]);
}
All of the above answers use the new Function() approach which is not recommended as it effects your app performance. You should totally avoid this approach and use window[classname] = function() { ... }; as #user3018855 mention in his question.
This is some JS code
var methodArr = ['firstFunc','secondFunc','thirdFunc'];
for(var i in methodArr)
{
window[methodName] = function()
{
console.log(methodName);
}
}
My problem is that how to get the name of a function in JS.
In JS, use this.callee.name.toString() can get the function name. But in this situation, it is a null value. How can i get the 'funName' string?
Sorry, I didn't make it clear.
I want to create functions in a for loop, all these functions has almost the same implementation which need its name. But others can call these functions use different name.I want to know what methodName function is called.
it seems a scope problem.
Try this:
var methodArr = ['firstFunc','secondFunc','thirdFunc'];
for(var i in methodArr) {
var methodName = methodArr[i]; // <---- this line missed in your code?
window[methodName] = (function(methodName) {
return function() {
console.log(methodName);
}
})(methodName);
}
window['secondFunc'](); // output: secondFunc
I have a bunch of useful functions that I have collected during my whole life.
function one(num){
return num+1;
}
function two(num){
return num+2;
}
I can call them with two(two(one(5)))
But I would prefer to use (5).one().two().two()
How can I achieve this without using prototype?
I tried to see how underscore chain works, but their code is too intense to understand it
The dot syntax is reserved for objects. So you can do something like
function MyNumber(n) {
var internal = Number(n);
this.one = function() {
internal += 1;
// here comes the magic that allows chaining:
return this;
}
// this.two analogous
this.valueOf = function() {
return internal;
}
}
new MyNumber(5).one().two().two().valueOf(); // 10
Or you're going to implement these methods on the prototype of the native Number object/function. That would allow (5).one()...
In order to avoid having to call toValue at the end of the chain as in #Bergi's solution, you can use a function with attached methods. JS will call toValue automatically when trying to convert to it a primitive type.
function MyNumber(n) {
function x () { }
x.one = function() { n++; return this; };
x.valueOf = function() { return n; };
return x;
}
Then,
MyNumber(5).one().one()
> 7
A nice and general alternative is creating a custom function composition function
var go = function(x, fs){
for(var i=0; i < fs.length; i++){
x = fs[i](x);
}
return x;
}
You can call it like this:
go(5, [one, two, two])
I am personaly not a big fan of method chaining since it restricts you to a predefined set of functions and there is kind of an impedance mismatch between values inside the "chaining object" and free values outside.
Another alternative is to use lodash flow function. For example:
var five = _.flow(one, two, two)
five(5)
I prefer assigning a new chain to a variable. It gives it a clear name and encourages re-use.
Btw, lodash also helps in passing additional arguments to the functions of the chain. For example:
var addFive = _.flow(
_.partialRight(_.add, 1),
_.partialRight(_.add, 2),
_.partialRight(_.add, 2)
)
There are many other useful functions to help in functional chaining, e.g., partial, spread, flip, negate, etc.
Basically there is no function composition in JS. Even if there had been, it would be in the reverse order of what you mention in your question. ie two . two . one because Math declares composition operator like that. The order you want is called piping.
Having said that if you really want composition with dot operator, you may still do it by overloading the . operator via the Proxy object. It's a slightly convoluted topic and you may check this nice blogpost for some ideas.
However the simplest approach for your need would be by reducing an array of functions as;
var pipe = (fs,x,y) => fs.reduce((r,f) => f(r),{x,y}),
fs = [ ({x,y}) => ( x++
, y++
, {x,y}
)
, ({x,y}) => ( x*=3
, y*=3
,{x,y}
)
, ({x,y}) => ( x--
, y--
, {x,y}
)
];
var {x,y} = pipe(fs,1,2);
console.log(x,y);