Having trouble understanding a reflection test in Javascript Koans - javascript

There is already an answer posted to the test itself, which can be found here, but I can't seem to figure out why that answer is correct.
The part of the test that is giving me trouble is:
var keys = [];
var fruits = ['apple', 'orange'];
for(propertyName in fruits) {
keys.push(propertyName);
}
ok(keys.equalTo(['__', '__', '__']), 'what are the properties of the array?');
The (apparently) correct answer, as noted in the above linked question is
ok(keys.equalTo(['0', '1', 'fruits.prototype'), 'what are the properties of the array?');
I tried inserting the answer - fixed the syntax error - and my test still fails.
In the same test file, another test is nearly identical and the answer is what I would expect it to be:
test("property enumeration", function() {
var keys = [];
var values = [];
var person = {name: 'Amory Blaine', age: 102, unemployed: true};
for(propertyName in person) {
keys.push(propertyName);
values.push(person[propertyName]);
}
ok(keys.equalTo(['name','age','unemployed']), 'what are the property names of the object?');
ok(values.equalTo(['Amory Blaine',102,true]), 'what are the property values of the object?');
});
The only difference I can see between these two tests is that the second is using an object rather than an array.
I ran the code from the first test by itself (outside of the unit testing framework) and output the value of keys, which it showed as ["0","1"] - what I would expect. Where is this hidden third value, and how can I access it?
So, I guess I ultimately have two questions:
Why is the answer from the other question not working for me?
What is different about the first test and the second test?

Disclaimer: I'm pretty sure this is right, but haven't bothered testing it. Could you try my answer out, since you've got the tests running?
Looking at the files on GitHub, there is a helper script called koan.js. I'm assuming it gets loaded before the tests because I am too lazy to run them myself :P. (It's in the support directory.)
In this file, there is a method called equalTo defined on all arrays:
Array.prototype.equalTo = function(compareTo) { ... }
So the answers to your questions:
Because the answer to the other question was wrong. Completely misguided. Etc.
Because the method is defined on Array rather than Object.
Seems a little bit underwhelming.
If you define a function like this on the prototype, it will be inherited by all arrays. Try defining something like that in the console and then evaluate [].equalTo. And then, for more fun, try something like:
for (x in []) console.log(x)
Since this method is defined on the prototype, the loop iterates over it as well. So the answer is to the test is probably 0, 1, 'equalTo'. However, if you use the for loop with the hasOwnProperty check, it will naturally not iterate over the method.
This is really an object lesson about no using for in to iterate over arrays :). You never know what's going to sneak in... Coincidentally, this is why prototype.js fell out of favor despite actually being a nice framework.

Related

Getting deformation object in spark ar environment

I am trying to get deformation object to change it properties by JS, but I cannot even get it by any Spark module.
Spark AR have sample project with face distortion deformation.
https://developers.facebook.com/docs/ar-studio/tutorials-and-samples/samples/face-distortion/
You can even see in tutorial, that there is some morph object attached, which called faceDistortionPack. This object is located in assets, and I tried to get it by different ways in script, but couldn't make it. I want to write some js logic to manipulate deformations.
const Scene = require('Scene');
const Diagnostics = require('Diagnostics');
const faceMesh = Scene.root.find('facemesh_distortion');
Diagnostics.log(faceMesh); // FaceMesh: https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/scenemodule.facemesh
Diagnostics.log(faceMesh.deformation); // null
Diagnostics.log(faceMesh.find('faceDistortionPack')); // Exception...
// ....
I want to get 'faceDistortionPack' object to access it properties, like 'nose_z', so I can change them by JS.
Although this is a pretty old question I thought I'd answer anyway if anyone struggles with this and comes across this thread.
First of all: There's an amazing collection of helpful tips, tutorials, snippets etc. called Spark AR Community. There you can find a GitBook with an alternative, better indexed and better working version of the official script object reference. I recommend using it if you're lost in the official reference or it's not working which happens quite often. There you can see, that the BlendShapesMesh, mentioned in the previous answer is deprecated as of Spark AR v85+, so that won't help anyone. ()
So, what you are trying to achieve, if I understand it correctly, is to access the faceMesh's Blendshapes and change their value through script. What you need to do is this:
Follow the instructions in this Tutorial: https://sparkar.facebook.com/ar-studio/learn/tutorials/face-distortion-retouching/
After you applied the Blendshapes, mess around a bit with adjusting the Blendshapes, so you understand what's happening.
Add a script to your assets. You're gonna want to access the faceMesh-Object's Blendshapes' weight Property through script. That can be done by using the getBlendshapes()-Method of the Mesh-Class.
Here's a code example:
//Require Modules
const Scene = require('Scene');
const Diagnostics = require('Diagnostics');
const Reactive = require('Reactive');
//Enable async/await [part 1]
(async function () {
//Load Assets as Promise
const loadAssets = await Promise.all(
[
Scene.root.findFirst("faceMesh0"), //Put name of your facemesh here, default is faceMesh0
]
).then(function(assets){
const faceMesh = assets[0]; //Assign the facemesh from the assets-Array of the promise to a variable you can work with better
const faceMeshShapes = faceMesh.getBlendShapes(); //Access all Blendshapes of the faceMesh-Object and store them in an Array
What you have now is a variable called faceMeshShapes, that's an object with an array of Blenshapes in it. You can console log it with Diagnostics.log(faceMeshShapes) and see that there's an array called "_value" in it, that's filled with all Blendshapes as objects, that have a weight-Property, which specifies the weight of the Blendshape with a scalarValue. You can consolelog this Value by converting it with the pinLastValue()-Method of the scalarValue-Class and you can assign different values by binding it to a custom value, that you convert to a scalarValue by using the val()-Method from the Reactive-Module.
Here's an example:
Diagnostics.log(faceMeshShapes._value[0].weight.pinLastValue()); //console log the Value of the weight property's scalarValue of the Blendshape that's stored at the index 0 of the _value-Array
faceMeshShapes._value[0].weight = Reactive.val(0.5) //set the weight to 0.5
Diagnostics.log(faceMeshShapes._value[0].weight.pinLastValue()); //console log the changed value of the weight property
So basically, that's how you can access every Blendshape's weight. The index should be according to the order in which they are listed in spar AR studio, beginning with 0 for the first BlendShape. Now you can do lots of things with this value, like binding it to an animation that animates it from 0-1 or to the mouth-openess using the face-tracking module and so on.
Last but not least, don't forget any semicolons and close all brackets.
});
})(); // Enable async/await [part 2]
P.S.: It can really help sometimes to understand what's happening and how to access stuff, by console logging a list of all properties of an object. Especially since the spark AR documentation is pretty weak on that part (and pretty weak in general). You can use the following function from MDN to do that. It's nothing fancy, but it does the job and has saved me a couple of times already :)
function listAllProperties(o) {
var objectToInspect;
var result = [];
for(objectToInspect = o; objectToInspect !== null;
objectToInspect = Object.getPrototypeOf(objectToInspect)) {
result = result.concat(
Object.getOwnPropertyNames(objectToInspect)
);
}
return result;
}
The link you posted isn't live any more.
I'm guessing the below links are more relevant now for others looking into this. I animated a series of blendShapes, and these links were useful. Basically cycling through the weights of each blendShape.
https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/scenemodule.blendshapesmesh
https://sparkar.facebook.com/ar-studio/learn/documentation/reference/classes/scenemodule.blendshape

Javascript: How can I use object properties in functions using the parameter?

I am a beginner to Javascript and I am trying to make a text-based game for practice. I have made objects for enemies like so:
export const spider = {
strength: 1,
health: 4,
gold: 1
};
I am now trying to make a function which can search for their stats when their name is entered in the field. It looks like this:
export const findStats = (target) => {
return target.health;
}
However, when I do this and test it I get 'undefined.' However, if I replace 'target.health' with 'spider.health' it works properly.
The error occurs when I try doing findStats("spider") which gives me the 'undefined results.'
Any help would be appreciated. Note: I did look around this site but found the threads too complex or not quite what I was looking for. I am a beginner so simple terms would be greatly appreciated!
Thanks for all your comments!
My issue is unlike to the one on the right as when I try it with my code is still returns 'undefined.' Here is the code that I have just tried using the other thread. Keep in mind I am a beginner so I am probably doing it very wrong but if you could help further I would be very happy.
export const findLocalStats = (target) => {
var bar = 'strength';
console.log(target[bar]);
}
Here are is my test command:
expect(findLocalStats("spider")).toEqual(1);
Furthermore, when I tried the code written in JSFiddle in my editor, it still gave the same error in the terminal which is 'undefined.'
export function findStats(target) {
return target.strength;
}
And the test command:
expect(findStats("spider")).toEqual(1);
And the exact error message:
findStats() gives the stats of the target wanted.
expect(received).toEqual(expected)
Expected value to equal:
1
Received:
undefined
Difference:
Comparing two different types of values. Expected number but received undefined.
Sorry if this is very tedious.
Note 2: I posted this question before but I have changed it to attempt to explain my issue better as I still do not understand the answers I was given and the links to duplicate questions - which I have tried to replicate. Furthermore, the duplicate that I was given was different. It used the object as the keyword, however I want to use it as a feed-in to a parameter so that it is versatile for more than one enemies if that makes sense. Thank you!
When you are calling findStats like this : findStats("spider") you are sending a string to the function as an argument. That's why when it tries to get the property health it says undefined.
You need to give the function an instance of your spider object. To create an instance you can use the Object.create() method like var killMe = Object.create(spider). This will create a new object based on the one you declared before. Then you can call your function like this : findStats(killMe)
You can find an exemple here : https://jsfiddle.net/10ex1xqt/

Javascript: Where do I put an array with standard data?

I'm a bit new to posting here, I've always found my answers by searching and never needed to create my own post, so if I'm doing something wrong please do tell me.
I also don't really know how to formulate the question, so I'm sorry if it's weird.
I'm writing an extension for chrome and I'm unsure about where I leave my array with country codes and names.
Right now it's just at the start of my javascript file (with all the other code under it). It's not really a problem but I feel like I can put it somewhere else. Should I make a seperate file for it or should I leave it the way it is?
It's just this:
var countryList = [
["AF", "Afghanistan"],
["AX", "Aland Islands"],
["AL", "Albania"],
["DZ", "Algeria"],
["AS", "American Samoa"],
["AD", "Andorra"],
["AO", "Angola"],
["AI", "Anguilla"],
["AQ", "Antarctica"],
["AG", "Antigua And Barbuda"],
["AR", "Argentina"],
["AM", "Armenia"],
// And then a whole bunch more...
];
I'm sure I can find the answer somewhere, but since I'm having trouble formulating it I can't seem to find any related issues.
It's best practice to put it either in a namespace or scoped within another function to keep it out of the Global namespace of the 'Window' object and not have any potential conflicts with other variables/libraries.
Here's an example of a namespace:
var App = {
countryList: []
};
var arr = App.countryList[0];
Here's an example of scoping:
(function() {
var countryList = [];
// do something here
})();
When scoping within a Function like the last example, the 'countryList' variable will only be available to other functions/code contained within the surrounding function. By the way, this is scoped/contained it within an "anonymous method" that is automatically executed after declaration, via currying.
More information about Currying: http://www.dustindiaz.com/javascript-curry/

Can you add a function to a hijacked JavaScript Array?

This question is related to What are the best practices to follow when declaring an array in Javascript?
Let's say a client, let's call them "D. B. Cooper", has a first requirement that the following code must run before any other JavaScript code:
Array = function(){
alert('Mwahahahaha');
};
Furthermore, Cooper requires that custom functions must be added to the built in Array object (not the hijacked one). For example, if Array was unhijacked, this would be done with:
Array.prototype.coolCustomFunction = function(){
alert('I have ' + this.length + ' elements! Cool!');
};
Which would afford:
var myArray = [];
myArray.coolCustomFunction();
However, this is not compatible with the first requirement. Thus, how can you best fulfill both of D. B. Cooper's requirements?
Note: D.B. even wrote a test fiddle to help make sure solutions meet his requirements...what a guy!
Update:
For those of you who like a challenge: please try to find an unhijackable cross-browser solution to this problem. For example, here's an even more hijacked test case (thanks for reformatting this Bergi) that hijacks Array, Object, Array.prototype.constructor, and Object.prototype.constructor. Thus far, it looks like there may be a browser-specific solution to this (see Bergi's comment on his answer, and let us know if you find a way to hijack it in FF), but it is unclear at this point if there is a cross-browser solution to this.
Whatever your Array function/constructor is, the literal syntax for arrays will always generate "real" arrays with their [[prototype]] set to the native array prototype object (once, this was a security vulnerability). So, you can always access that by using
Object.getPrototypeOf([])
even if Array or [].constructor are hijacked. (Will of course not work when Object is hijacked, then it get's really complicated)
(Brought D.B. down!)
If you want to use a workaround, in FF the following line will always work (and is not hijackable):
[].__proto__.coolCustomFunction = coolCustomFunction;
Since Array is not necessarily equal to [].constructor, you could use [].constructor to refer to the original Array function since this is hardwired and Array = function(){} won't alter it.
Array = function () { alert("foo")};
// this will always point to the original Array
[].constructor.prototype.foo = "bar";
var myArray = [0, 1];
alert(myArray.foo) // alerts "bar"
http://jsfiddle.net/yXPJ8/5/
Yes ... you just did ... but you created the array using [] .. if you use new Array() it works fine ...
See example here

Using jasmine to compare arrays fails in ie8

Apparently ie8 has three properties that get appended to the resulting array from a call to String.prototype.match():
input, index and lastIndex
(MSDN Documentation)
The result is that array comparison fails when using Jasmine's .toEqual() matcher.
I'm still working my way up the learning curve on unit testing, so I'm just curious of what the right way is to do deal with this failure.
The following works but seems a bit lame:
numArray = str.match(/\d+(\.\d+)?/g);
if (numArray && numArray.input) {
delete numArray.index;
delete numArray.input;
delete numArray.lastIndex;
}
Underscore's 'difference' method can help -
expect(_.difference(['item1', 'item2'], ['item1', 'item2'])).toEqual([]);
http://underscorejs.org/#difference
I think #monkeyboy's answer is not correct.
Since underscore.difference() returns the elements of the first array that are not present in the second array: _.difference([1],[1,2]); is also [] so the test will pass when it shouldn't. I couldn't find a way to solve this using underscore.
So i'm using:
expect(JSON.stringify(result)).toBe(JSON.stringify(expected));
which works as expected.
Anyway, i'd like to know how others are doing this.

Categories

Resources