I am trying to use some code from this tutorial and it contains some strange javascript notation that I am not familiar with chart.attr = function(name, value) {... . More than it being unfamiliar to me, it is throwing errors. I am trying to figure out how it can be changes to work in pure javascript.
function LineChart(config) {
function chart() {
// Draw the line.
chartContainer.append("path")
.datum(p.data)
.attr("class", "line")
.attr("d", line);
}
// **** This is the notation I do not understand, and gives me errors ****
chart.attr = function(name, value) {
if (arguments.length == 1)
{
return p[name];
}
else if (arguments.length == 2)
{
p[name] = value;
}
return chart;
}
chart.update = function() {
}
return chart;
}
Your code is trying to use a variable p which is undefined. It should be defined in the LineChart function as:
function LineChart(config) {
var p =
{
parent : null,
labels : [ "X", "Y" ],
...
};
...
}
As for the notation that you don't understand, this is an anonymous function expression which is being assigned to the chart.attr property. Even though it can be called by chart.attr(), this is still an anonymous function because it doesn't have a name.
The purpose of this particular function is to be a getter and setter for properties of the p object. It looks at the arguments to determine the way the function should behave: if there is only one argument, then it needs to return the property value, if there are two arguments then it should set the property value.
Example usage would look like:
var c = new LineChart();
var parent = c.attr('parent'); // get the value of the parent property
c.attr('parent', $('#something')); // set the value of the parent property
Let's dissect that line of code:
//Define chart.attr as a function that by default takes 2 parameters;
chart.attr = function(name, value) {
//If the function only gets 1 argument (so the first one)
if (arguments.length == 1)
{
//return the element with key "name" from the array p
//effectively a getter
return p[name];
}
// else, check if there are 2 arguments, but no more
else if (arguments.length == 2)
{
Assign the value of "value" to the element with key "name" from p
effectively a setter;
p[name] = value;
}
//at the end, return the chart
return chart;
}
So what this piece of code does is that if you pass only 1 argument to chart.attr(), it retrieves the value associated with that key from the array p. If you pass 2 arguments, it uses the second argument as the value of the key-valuepair from the array p with the first argument as the key.
now, without knowing the error you get, it's hard to debug this. However, the only way in which this would give an error is if p is undefined. if p doesn't contain that key, it returns null if it's a getter, and creates if it's a setter.
There is another way for this code to fail. And since the op didn't provide the error I will just speculate.
This can fail if you call, for example, chart.attr('somekey','somevalue') before chart.attr = function(name,value) { } is executed. This happens because of function hoisting...you are assigning a value to a property in this line of code. You're not defining a function...you're assigning one.
If you call chart.attr('somekey','somevalue') in the above conditions, you'll get a chart.attr is not a function error.
Related
In You don't know JS type & grammar, a design pattern called "dependency injection" is shown in the end of the chapter 1, which I know is not point of this chapter, but I was confused by the example.
the example code is here:
function doSomethingCool(FeatureXYZ) {
var helper = FeatureXYZ ||
function() { /*.. default feature ..*/ };
var val = helper();
// ..
}
So I want to use this function.
Because I don't know whether the FeatureXYZ exists, I dont know how to use it.
doSomethingCool() will not use the FeatureXYZ and doSomethingCool(FeatureXYZ) will throw error if no FeatureXYZ exists. Thus, the function may be a meaningless function.
You can use default parameters to set a default function to call if no parameter is passed, or use the parameter instead if passed to the function.
Use bracket notation to reference the property "FeatureXYZ" at either window object or the object which "FeatureXYZ" is expected to be a property. Bracket notation should not throw an error if the property is not defined at the object.
function FeatureXYZ () {
return {def:456}
}
function doSomethingCool(FeatureXYZ = window["FeatureXYZ"] /* obj["FeatureXYZ"] */) {
var helper = FeatureXYZ || function config() {
/*.. default feature ..*/
return {abc:123}
};
var val = helper();
// ..
return val
}
console.log(doSomethingCool());
Basically, the || operator returns the first value if it's not null or undefined. If it is, it returns the second value. Example:
var x = null;
var y = x || 5;
x is null, hence z will be set equal to 5. If x would be, for example, 4, y would be set to 4 as well.
JavaScript has the feature (or oddity, depends on the viewer), that you do not have to pass every parameter a function has. For example, if you have the following function:
function(x, y) {
return x * (y || 5);
}
It would not result in an error if you call the function without passing y, because it would multiply x by 5 if y is not provided (and therefore undefined).
This is how the example works: It sets helper equal to FeatureXYZ, or, if it is not passed as an argument, e.g. doSomethingCool() (and therefore undefined), it is set to a default function. That way later in the code, when you execute helper(), you either use the passed function, or the default one, if it's not given.
I have QML code, for example this code
Item {
id:self;
function update(){
var visitFunc = self.applyUpdate;
innerTraversal(self,visitFunc);
}
function reset(){
var visitFunc = self.applyReset;
innerTraversal(self,visitFunc);
}
function innerTraversal(obj, visitFun){
console.log(typeof visitFun);
if(obj!== self && visitFun && typeof visitFun ==="function")
visitFun(obj);
if(hasChilderns(obj)){
var objChilderns = obj.children;
for(var i=0 ; i< objChilderns.length ; i++){
innerTraversal(objChilderns[i]);
}
}
}
function hasChilderns(obj){
if(typeof obj.children !== 'undefined')
return true;
else
return false;
}
function applyReset(obj){
if(typeof obj.reset === 'function')
obj.reset();
}
function applyUpdate(obj){
if(typeof obj.update === 'function')
obj.update();
}
}
in normal javascript this works cool, but when I use this code in QML the bad thing is visitFun always has type of undefined, and it does not work..
any idea how to make this work ?
In QtQuick 2 you should be able to bind functions to properties using
Item { //<-- declaration
id : item
property variant fun
}
item.fun : Qt.binding(function(){doSomething()}) //<--defintion
item.fun // <-- invocation without braces
So you could pass an object with a generic function as parameter.
In general, function overloading a can also be used to create a generic function for example to create a Button type:
---Button.qml
Item {
function fun() {} //<-- declaration (empty dummy)
MouseArea {
anchors.fill: parent
onClicked: {
fun(); //<-- invocation
}
}
}
---
---main.qml---
Button {
id: button
function fun() { //<-- defintion by overloading
doSomething
}
}
---
Clicking the button will activate its onClick handler and actually do something ;). Again, you would then pass the object with the generic function not the function itself.
In QML internals your "self" has type "QQuickItem", while normal JS object (created with "new Object()" or "{"prop" : "value"}", for example) has type QJsValue. And "self" isn't variable name, it's QML id, keep in mind that defference.
In QML the using of signals\slots or property bindings can be much more powerful rather than passing callback like in "normal" JS. The using of "typeof" is also bad practice (as far as I know in "normal" JS too), you can simply write something like:
// Or simply "return obj.children" - but function become useless than.
if(obj.children)
return true;
else
return false;
But this code still useless - Item's property "children" in QML has type "list", it always exists.
So my conclusion - you should try to learn some QML basics before writing something.
Okay so I have an object and I want to apply a callback function to all of the methods in the object. This is what I have tried so far:
var namespace = {
foo : 'bar',
foobar : function() { console.log('call from foobar!')},
someFunc : function() { console.log('call from someFunc!')},
someFunc2 : function() { console.log('call from someFunc2!')}
}
var logger = {
_callback : function () {
console.log('call from logger!',arguments);
}
}
for (var m in namespace) {
if ( namespace.hasOwnProperty(m) && (typeof namespace[m]=='function') ) {
logger[m] = namespace[m];
namespace[m] = function() {
logger._callback(arguments);
logger[m].apply(this, arguments);
}
}
}
namespace.foobar('foo');
namespace.someFunc('bar');
namespace.someFunc2('bar2');
This is what is getting logged to the console:
call from logger! [["foo"]]
call from someFunc2!
call from logger! [["bar"]]
call from someFunc2!
call from logger! [["bar2"]]
call from someFunc2!
As you can see, for some reason all 3 methods of namespace are outputting 'call from someFunc2! which is wrong. I'm not sure what the issue here is.. what am I doing wrong?
Try
for (var m in namespace) {
if ( namespace.hasOwnProperty(m) && (typeof namespace[m]=='function') ) {
logger[m] = namespace[m];
(function(index){
namespace[index] = function() {
logger._callback(arguments);
logger[index].apply(this, arguments);
};
})(m);
}
}
otherwise the namespace[m] = function(){} will use whatever m is last
There's just one "m". The code inside that function you create in the for loop references the "live" value of "m", not a value frozen at the point the function was created. The last value it takes on is name "someFunc2", so that's the one that's called.
Step by step:
You create the "namespace" and "logger" objects.
The loop runs. The variable "m" takes on the successive values of the properties in the "namespace" object, and creates a new function for each relevant property of that object.
At the end of the loop, "m" has the value "someFunc2".
You call one of the "namespace" functions. That'll be a call to one of the functions created in the loop. That function will in turn call the "_callback" function. And now the important key point: it references a property of the "logger" object using the value of "m". What is the value of "m"? It's "someFunc2".
var currentbutton = {};
function setPreset(obj) {
try{
if(obj.name===name && obj.value===value){
//log.error("preset array's OID at position ["+index+"] is"+presets[index].name +" and the value stored is "+presets[index].value);
currentbutton.name=obj.name;
currentbutton.value=obj.value;
log.error("currentbutton name= "+currentbutton.name+ "currentbutton value= " + currentbutton.value );
}
else
log.error("adklafjklajkl");
}
catch(ie){
log.error("couldn't set preset");
}
presets.forEach(function(obj));
I know there must be mistakes in this code that I wrote, first of all, I was told that the function need to receive an object as an argument, which I have no idea how to pass it to the function. I tried google, but I did not find any relevant information on whether a function can receive an object as an argument.
presets is an array which contains objects which has two properties (called "name" and "value")
basically, the array Presets goes through its enumerated list of variables with forEach, and compare if the argument obj's name and value are identical or not to any of the objects stored inside the array, if they are identical, set the currentbutton's name and value to the one inside the argument obj. Then we will have other functions which will operate on currentbutton that i don't have to worry about.
I know it's not really clear because I am not even sure if that's what is wanted of me.
You don't quite understand how forEach works. The forEach method takes a function as its argument:
[1,2,3].forEach(function(item) {
alert(item);
});
That function passed into forEach is given an argument itself. Here, I've named it item. The forEach method repeatedly invokes the function and supplies a successive member of the array as the first argument each time it is invoked.
Now, instead of passing in a literal function, I can use a variable to hold my function:
var alertStuff = function(item) {
alert(item);
}
Then, I use that function (referring to it by variable name) in forEach:
[1,2,3].forEach(alertStuff);
// is the same as...
[1,2,3].forEach(function(item) {
alert(item);
});
Thus, you want to use presets.forEach(setPreset);.
Define a function which accepts a paramter
function myNewFunc(obj){
alert(obj.myFirstProp);
}
Define an object which we are going to pass as an argument to the above function
var myObject = {
myFirstProp: "testing"
};
Call the function and pass the object as an argument
myNewFunc(myObject);
Your brackets were screwed up and you invoked forEach wrong.
var presets = [
{name:'a', value:1},
{name:'b', value:2},
{name:'c', value:3},
];
var currentbutton = {};
function setPreset(obj) {
try{
if(obj.name===name && obj.value===value){
//log.error("preset array's OID at position ["+index+"] is"+presets[index].name +" and the value stored is "+presets[index].value);
currentbutton.name=obj.name;
currentbutton.value=obj.value;
log.error("currentbutton name= "+currentbutton.name+ "currentbutton value= " + currentbutton.value );
} else { // syntax error, opening { of else block was missing
log.error("adklafjklajkl");
}
} // syntax error, closing } of try block was missing
catch(ie){
log.error("couldn't set preset");
}
} // syntax error, closing } of function was missiong
presets.forEach(setPreset);
I have found that not using anonymous functions has made my code more readable and self-documenting by flattening the code into more understandable, standalone functions. So I'd like to break out the following construct from:
function Save() {
myVal = 3.14 // some arbitrary value
$('#myID').each(function(index,element) {
if ($(this).val() === myVal) {}
});
}
Into:
function Save() {
myVal = 3.14 // some arbitrary value
$('#myID').each(myFunction);
}
function myFunction(index,element) {
if ($(this).val() === myVal) {}
}
The problem with using .bind here, is that you lose the value of $(this) inside the each method, so (I don't think) I can bind myVal to myFunction.
Maybe I could use element instead of this?
Edit 1: I should have used .myClass instead of #myID for an example selector.
Edit 2: I'm not using bind in the proposed solution because I don't think bind would work.
Edit 3: I appreciate everyone saying that the first example is more readable. I'm just exploring the language and trying out different thoughts.
And what about :
function Save() {
myVal = 3.14 // some arbitrary value
$('#myID').each(myFunction(myVal));
}
function myFunction(myVal) {
return function(index, element) {
if ($(this).val() === myVal) {}
}
}
You're not losing access to this; you're losing access to myVal because myVal is not known inside myFunction, mainly due to that function being defined in a scope that does not have a definition for myVal.
What you can do is something like this:
function myFunction(index, element, myVal) {
if ($(this).val() === myVal) {}
}
and then:
function Save() {
myVal = 3.14 // some arbitrary value
$('#myID').each(function(index, element) {
myFunction.call(this, index, element, myVal);
});
}
This way if you have a lot of logic inside myFunction, you can still separate it out and just call myFunction from .each)'s callback. Not that myFunction is being called with .call because that way you can pass in an explicit value for this (the first argument). Hence this is the same this that is inside the callback to .each.
To be honest though, the first option is much more readable and you really aren't gaining much by splitting your code out like this.
this in this context will be the same. The one thing you lose access to is myVal. You are right that you can't use Function.bind because that does not allow you to specify to keep the original (call time) this
Here's how you can pass myVal and keep the same this, using a modified version of Function.bind, that we're calling myBind
/**
* Binds the given function to the given context and arguments.
*
* #param {function} fun The function to be bound
* #param {object} context What to use as `this`, defaults
* to the call time `this`
* #param {object[]} customArgs Custom args to be inserted into the call
* #param {number} index Where to insert the arguments in relationship
* to the call time arguments, negative numbers count from the end.
That is, -1 to insert at the end. Defaults to a 0 (beginning of list).
*
*/
function myBind(fun, context, customArgs, index) {
return function() {
// Default the index
index = index || 0;
// Create the arguments to be passed, using an old trick
// to make arguments be a real array
var newArgs = Array.prototype.slice.call(arguments, 0);
// Tack the customArgs to the call time args where the user requested
var spliceArgs = [index, 0].concat(customArgs);
newArgs.splice.apply(newArgs, spliceArgs);
// Finally, make that call
return fun.apply(context || this, newArgs);
}
}
function Save() {
myVal = 3.14 // some arbitrary value
$('#myID').each(
// myFunction wil be called with myVal as its last parameter
myBind(myFunction, null, [myVal], -1)
);
}
function myFunction(index, element, myVal) {
if ($(this).val() === myVal) {
// do it here
}
}
To demonstrate the flexibility of this function, let's bind more than one argument, and it should be inserted at the beginning of the call time arguments
function Save() {
var myVal = 3.14, val2 = 6.28; // some arbitrary values
$('#myID').each(
// myFunction wil be called with myVal and val2 as its first parameter
myBind(myFunction, null, [myVal, val2], 0);
);
}
// Since I don't need element, it's already available as this, we don't
// declare the element parameter here
function myFunction(myVal, val2, index) {
if ($(this).val() === myVal || $(this.val() === val2)) {
// do it here
}
}
This is almost the same answer as Samuel Caillerie. The only difference is that we create a different version of Function.bind that doesn't bind this, just the arguments. Another benefit of this version is that you can choose where the insert the bound arguments;