Function assignment doesn't work - javascript

I'm new to Node.js. I have searched the forum but couldn't find a similar question. Here is the problem I encountered. The following code runs fine.
process.stdout.write("hello world!\n");
but the following code:
var myprint = process.stdout.write;
myprint("hello world");
will generate the following error:
TypeError: Cannot read property 'defaultEncoding' of undefined
Any suggestions? Thank you so much.

Probably, the write() method needs to be called with the correct object reference so the write() method knows what stream it is writing to. There are multiple ways to work around this. Here's one way:
var myprint = process.stdout.write.bind(process.stdout);
myprint("hello world");
See .bind() on MDN for more info.
For future reference, when you do:
var myprint = process.stdout.write;
myprint only contains a reference to the write method and that method is then called with no object reference. That means that the this pointer inside the write() method will not point at the stdout stream like it would when you call it as process.stdout.write(). If a method needs it's instance data (which most methods do), then this creates a problem. You can "bind" the object reference to a new temporary function using .bind() which allows you to assign it to another variable and then use it directly like you were attempting to do.

The write function is trying to access a variable on the this variable, which is not set to process.stdout when you call myprint, unlike when you call process.stdout.write.
Note that
var out = process.stdout;
out.print('wow\n');
will work as expected.

Related

Callback function missing scope for required data

I have a issue with a callback function's variable losing its scope
.
I have the following 2 objects in an array(simplified down to show the problem)
const search = [{socket: new WebSocket('ws://live.trade/123')},
{socket: new WebSocket('ws://live.trade/xyz')}];
I then do a forEach on them in an attempt to log the sockets url once the socket is open.
search.forEach(function(element){
element.socket.on('open', function open() {
console.log(element.socket.url);
});
});
*actual output*
ws://live.trade/xyz
ws://live.trade/xyz
*expected*
ws://live.trade/123
ws://live.trade/xyz
I feel like the reason is that when the function open() runs element is not in scope and it just uses whatever was last there(being the ws://live.trade/xyz).
Is this correct? And finally what would be the way to go about fixing this? The real use for this is when the socket is opened I need to send version data to the server via the socket that called it... Im going to have many sockets in reality and dont want to write a "socket.on('open'...)" for each individual one.
Any suggestions?
Thanks so much!
Your callback (i.e. in socket.on()) uses the element variable of the forEach(). From your actual result, it could mean that:
1. Scope issue
The variable could be "overriden" over the iterations because of javascript/node scope mechanism, it is not the case with the code you gave but it is a common issue working with scopes inside loops.
in this case you would have to do :
search.forEach(function(element){
element.socket.on('open', function(){
console.log(this.socket.url);
}.bind(element));
});
Explanation:
bind() passes its first argument (the element variable in this case) to the anonymous function as this inside it.
Inside your callback function, this.socket is equivalent to element.socket at the appropriate iteration
[UPDATE] after pointed out by Felix Kling, forEach() provides a scope for each element.
See bind for more details.
2. Dependency issue
If it is not a scope issue, it could mean that the library you are using does not work as expected. Maybe, try to log element inside the forEach() to check if the WebSocket objects are what you expect.
edit
For further reading on node/js loops and reference: see here
For node/js closures in general: see here

nodejs: short alias for process.stdout.write

I'm learning nodejs (and I like it!). I tried to figure out how to have shorter alias for console.log and I found out that I can use var cout=console.log and use cout('[string]') from then on. Then when I wanted to use process.stdout.write and I tried to make a short alias for it too, using var out=process.stdout.write. But when I use out('[string]'), I get the following error:
_stream_writable.js:220   var state = this._writableState;
                  ^   TypeError: Cannot read property '_writableState' of undefined
    at Writable.write (_stream_writable.js:220:19)
    at Socket.write (net.js:670:40)
    at Object. (/home/shayan/Desktop/nodejs/server.js:12:1)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:423:7)
What is wrong here?
How can I correctly create a short alias for process.stdout.write?
Thanks
You should not do this kind of "short alias". It's very messy and people reading your code won't understand why you use random function names instead of console.log. However, if you really want to create function aliases, consider using a function:
function out(text) {
// ^ ^- argument accepted by the function
// |------ the function name
process.stdout.write(text)
// ^- pass the argument you accepted in your new function to the long function
}
I added some explanation in case you don't know how a function works, you can safely remove it.
Edit:
The reason why it's not working is in the source code of Node.JS. The stacktrace you are getting back points to this line:
Writable.prototype.write = function(chunk, encoding, cb) {
var state = this._writableState;
// ...
}
It tries to reference a variable called _writableState from this. As written here:
Inside a function, the value of this depends on how the function is called.
This means, that this refers to process.stdout when you call process.stdout.write, however it is undefined, when you call it from your alias. Therefore you get a Cannot read property '_writableState' of undefined exception (as undefined does not contain that variable, which is important for the write function to execute).
Aside from a function declaration you can also use Function.prototype.bind:
const out = process.stdout.write.bind(process.stdout);
out('foo');
bind returns a new function with the context (this) bound to whatever value you pass.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

Reference to object not same as the object?

Why this works:
var i = document.createElement("input");
document.body.appendChid(i);
But not this:
var i = document.createElement("input");
var f = document.body.appendChild;
console.log(f === document.body.appendChild); //outputs true
f(i);
And the error details is:
TypeError: 'appendChild' called on an object that does not implement interface Node.
In JavaScript, what looks like a "method" doesn't actually "know" what object it's attached to; essentially, it's just a function which happens to have been saved as a property of some object. The determination of what object this will represent happens when you call it, in most circumstances based on the object to the left of the . in the call.
So your variable f points at the right function, but when it's called, it will see the wrong value of this. Since in this case it expects to be called on a DOM Node (in the working test, document.body), calling it outside of that scope raises the error shown.
Note that the above is all slightly simplified, with just enough detail to explain your example. You can probably find further reading by searching for explanations of this as well as call and apply, which are ways of explicitly setting the this binding of a function call.

Best approach to avoid javascript's “this” mistakes in XBL

Talking about XBL is not exactly talking about javascript. So I'll create this question that's related to this one, but now about XBL, where I'm not the creator of the root javascript code, but just of the methods and event handlers inside the bindings.
--
In some cases, the this keyword may not refer to the object I expect it to. (recent example: in an key event, in my XBL)
What's the best approach to avoid this kind of mistake?
For now, I'm using always the getElementById (or $.fn from jQuery), but I'm not sure if it's the best approach.
--update
A little bit more details:
Within a XBL method the only way to access the element that was defined in the Xul file (the GUI description file) without using "this" (as it may not be the "this" I expect) is with getElementById, and this makes the code not reusable, so I'm looking for alternatives..
As you've heard elsewhere, the problem is that the this parameter isn't necessarily the object you first thought of, especially when you're talking about event handlers and the like. The best way I found is to write a small function that will bind some object as the this variable and return another function that uses the binding to call the original function.
Take a look at this code:
var bindFunction = function(self, f) {
return function() {
return f.apply(self);
};
};
var foo = function() {
alert(this.hello);
};
var bar = {
hello: "Hello there"
};
var boundFoo = bindFunction(bar, foo);
boundFoo();
What I have is a function called bindFunction that takes an object (called self) and some function, and returns another function that will call the passed-in function applying the self object as the this variable.
The rest of the code is just some test code: a foo function that will alert this.hello, a bar object with a hello member, and how you can bind foo with bar.
In essence, I've taken a function that accepts no parameters and generated another function that will ensure the first is always called with the correct this variable. Ideal for event handlers.
(Sorry, but I know nothing about XBL, so I hope this will be of use.)
If there's not a best answer, a good approach could be use javascript files instead of XBL for the implementation. Looking the Xulrunner source code, looks like most code does this.
But I still would like to use XBL, if there was a way to work fine with it..

common reasons for javascript reference error with prototype

I am new to the prototype framework and fairly new to Javascript (it's been a long while sicne any heavy javascript work). I have defined a class that is calling another method within it and I keep getting a ReferenceError "getLabel is not defined". getLabel is another class method that I am trying to call. Here is the code where I am calling it:
var title = new Element('td');
title.addClassName(label_class);
title.addClassName('scorecard_tee_title');
title.appendChild(new Element('span').update(getLabel(tee, true)));
I have tried using this.getLabel as well but to no avail. I'm guessing that I have a syntax error in my class (which is too large to place here), but can't find what's wrong.
What should I be checking for? Are there any common reasons for this error to be thrown that I should keep in mind when using Prototype?
Thanks!
UPDATE: I pulled the getLabel function out of my class and it works fine, which leads me to believe that it is in some way related to the getLabel function being passed into or evaluated as a member of the class and not a standalone function. Still not sure what I'm doing wrong though.
It is my understanding (which I admit, is pretty limited when it comes to object oriented programming), that you don't call methods as functions directly. They need to be called as methods for an object. For instance:
getLabel(foo);
If getLabel is method of a class, it modifies an object. In your example, you are not modifying an object, so what is getLabel actually getting the label for? If it's getting the label of tee, then you'd want:
tee.getLabel();
But if I'm wrong, somebody let me know and I'll delete this.
You need to reference getLabel somehow, either classInstance.getLabel() or this.getLabel(). I see you already tried the later so I'm assuming this code is not running inside the same class as getLabel() belongs to?
The problem with the undefined methods was being caused because the method that was calling them were being called from anonymous function. Binding the anonymous function to (this) from where it was being created works.
I realized this when I looked at 'this' in Firebug and realized it was pointing to Window and not Object.

Categories

Resources