This ES6 code:
const log = () => console.log('hi');
const parent = (log = log) => log();
parent();
Transpiled to:
var log = function log() {
return console.log('hi');
};
var parent = function parent() {
var log = arguments.length <= 0 || arguments[0] === undefined ? log : arguments[0];
return log();
};
parent();
Gives error:
return log();
^
TypeError: log is not a function
The problem is this line:
const parent = (log = log) => log();
Because the argument name is same as its default parameter.
This works:
const log = () => console.log('hi');
const parent = (logNow = log) => logNow();
parent();
Is this a bug in Babel or is this not allowed in the spec itself?
Seems like this is the expected behavior of ES6.
Tested on the Chrome console, also got an error.
The ES6 spec is saying to that point:
Let parameterNames be the BoundNames of formals.
http://www.ecma-international.org/ecma-262/6.0/#sec-functiondeclarationinstantiation
This means when you create function, ES6 will do basically the same like babel is doing, it will manage the assignment of the params in the new context.
In javascript, when you create a variable a in a closed scope, global a, cannot be accessed anymore, because JS will take the a from the nearest possible scope, in AST.
Simple example:
var a = 1;
function test() {
// creates the variable a, and try to assign a value to it,
// because `a` is now available in the scope itself, it will take value of a in the inner scope, not the outer scope one
var a = a;
console.log(a)
}
test() // undefined
Why its not taking the value of outer a, and then assign it to the inner a, is because of hoisting, basically its doing this:
function test() {
var a; // the address for the variable is reserved at compile time
a = a; // run-time assignment
}
It takes all the variables declarations of a function and hoist it to the begin of the function.
This is the reason, why something like this will work:
function hoistingTest(n, m = 2) {
// return immediately
return multi(n);
// This declaration will be hoisted to the begin of the body
function multi(n) { return m * n }
}
Related
I need the current function name as a string to log to our log facility. But arguments.callee.name only works in loose mode. How to get the function name under "use strict"?
For logging/debugging purposes, you can create a new Error object in the logger and inspect its .stack property, e.g.
function logIt(message) {
var stack = new Error().stack,
caller = stack.split('\n')[2].trim();
console.log(caller + ":" + message);
}
function a(b) {
b()
}
a(function xyz() {
logIt('hello');
});
You can bind function as its context then you can access its name via this.nameproperty:
function x(){
console.log(this.name);
}
x.bind(x)();
After little research here is a good solution :
function getFnName(fn) {
var f = typeof fn == 'function';
var s = f && ((fn.name && ['', fn.name]) || fn.toString().match(/function ([^\(]+)/));
return (!f && 'not a function') || (s && s[1] || 'anonymous');
}
function test(){
console.log(getFnName(this));
}
test = test.bind(test);
test(); // 'test'
Source : https://gist.github.com/dfkaye/6384439
Building on #georg solution, this one returns just the function name. Note though that it may fail if called from an anonymous function
function getFncName() {
const stackLine = (new Error())!.stack!.split('\n')[2].trim()
const fncName = stackLine.match(/at Object.([^ ]+)/)?.[1]
return fncName
}
function Foo() {
console.log(getFncName()) // prints 'Foo'
}
A simple solution to dynamically retrieve function names [like magic variables] is the use of scoped variables, and the Function.name property.
{
function foo() {
alert (a.name);
}; let a = foo
}
{
function foo2() {
alert(a.name)
}; let a = foo2
};
foo();//logs foo
foo2();//logs foo2
Note: Nested functions cease to be source elements, and are hence not hoisted. Also, this technique cannot work with anonymous functions.
If (like me) you want to define this elsewhere and call it generically, you can store the code as a string somewhere global or import it, then eval() it wherever to access the current function name. (Using eval keeps the context at the point of invocation.)
There's gotta be a way to do this without using a string, but whatever.
SomeObject.whatFunc =
'const s = new Error().stack;' +
"const stackLine = new Error().stack.split('\\n')[2].trim();" +
'const fncName = stackLine.match(/(?<=at )(.*)(?= \\()/gm)[0];' +
'console.log(fncName);'
// Whereever you want the func name
function countBananas('weeee') {
eval(SomeObject.whatFunc)
// blah blah blah
}
countBananas() // logs 'countBananas'
just an update to get the full name :
function logIt(message) {
var stack = new Error().stack,
// update is on this line
caller = stack.split('\n')[2].trim().split(/\s+/)[1];
console.log(caller.trim().split(/\s+/)[1];);
}
function a(b) {
b()
}
a(function xyz() {
logIt('hello');
});
Here's what I mean. I'm practicing my object-oriented Javascript (I learn by practice), created the following class for fun
function Funcstack ( )
{
this.stack = []; // stack/array of functions w/ no params
this.pushFunction = function ( f )
{
// f: function w/ no params
this.stack.push(f);
}
this.popFunction = function ( f )
{
// f: function w/ no params
var n = this.stack.length;
if (n > 0) this.stack.splice(n - 1, 1);
}
this.executeFunctions = function ( )
{
// execute functions from top to bottom of stack
for ( var i = (this.stack.length - 1); i >= 0; --i )
{
var thisfunc = this.stack[i];
thisfunc();
}
}
}
var fs = new Funcstack();
fs.pushFunction(function() { console.log('z'); });
fs.pushFunction(function() { console.log('y'); });
fs.pushFunction(function() { console.log('x'); });
fs.executeFunctions(); // should print 'xyz'
and was surprised that it worked. The main reason is because I thought that, for example, in
this.pushFunction = function ( f )
{
// f: function w/ no params
this.stack.push(f);
}
the body of the function wouldn't recognize this.stack because this in the particular context refers to the invoking function pushFunction which doesn't have a member named stack! So am I correct in thinking that it looked up the scope chain? That seems to contradict the whole idea of this, though ... What's the point of it?
The short answer is "No". An execution context's this is always resolved in the current execution context, therefore it has nothing to do with scope. With broad arrow functions, it's set to the same value as the outer execution context.
A function's this is set by how the function is called or by setting with bind. It's not set lexically (where the call is in the code) or where the function is called from.
Where this is an object (always in non–strict mode) then its properties are resolved in the same way any object's properties are resolved, firstly on itself, then on its [[Prototype]] chain. Where a function is called as a method of an object:
fs.executeFunctions();
then the base object (fs) is set to this within the function. Therefore, when this.stack.length is resolved, this references the fs instance and executeFunctions is resolved as a property of fs.
The reason it works is because of the calling context.
this.stack = []; // <--- is an instance property ( member ) of any instance of Funstack
When you invoke "pushFunction" using "fs" ( the instance reference ) to reference
"this" ( the context ) becomes whatever "fs" object is..
If it still unclear, try reading more on javascript's "calling context".
When you say: var fs = new Funcstack();, you're declaring an object that is an instance of the class Funcstack. Note that inside the constructor, you are declaring some properties and methods of it.
So, the keyword this in all those methods will refer to the object you've created. You can change the context by using call, apply or bind if you want, at anytime.
Note that you are declaring the pushFunction as a member of the class by saying: this.pushFunction = function(f) { /* (...) */ }. So, you're creating a method into the class. That's why the this keyword refers to the object that is an instance of that class.
If you declared the function as var pushf = function(f) { /* (...) */ }, and then called that function inside some method of the class Funcstack, you would get the error:
TypeError: this.stack is undefined
Take a look at the example below.
function Funcstack() {
this.stack = []; // stack/array of functions w/ no params
var pushf = function _pushFunction(f) {
this.stack.push(f);
}
this.pushFunction = function(f) {
// f: function w/ no params
pushf(f);
}
this.popFunction = function(f) {
// f: function w/ no params
var n = this.stack.length;
if (n > 0) this.stack.splice(n - 1, 1);
}
this.executeFunctions = function() {
// execute functions from top to bottom of stack
for (var i = (this.stack.length - 1); i >= 0; --i) {
var thisfunc = this.stack[i];
thisfunc();
}
}
}
var fs = new Funcstack();
fs.pushFunction(function() {
console.log('z');
});
fs.pushFunction(function() {
console.log('y');
});
fs.pushFunction(function() {
console.log('x');
});
fs.executeFunctions(); // should print 'xyz'
Can anyone explain to me why "b" returns undefined and how I can get around this problem? Why does the "this" scope get lost when I call prototype functions by reference?
MyClass = function(test) {
this.test = test;
}
MyClass.prototype.myfunc = function() {
return this.test;
}
var a = new MyClass('asd').myfunc();
var b = new MyClass('asd').myfunc;
// Returns "asd" correctly
console.log(a)
// Returns undefined??
console.log(b())
=== EDIT / SOLUTION ===
As plalx writes, the correct solution in my case is to use .bind(). So the result looks like this:
MyClass = function(test) {
this.test = test;
}
MyClass.prototype.myfunc = function() {
return this.test;
}
var a = new MyClass('asd').myfunc();
var b = new MyClass('asd'),
bfunc = b.myfunc.bind(b)
// Returns "asd" correctly
console.log(a)
// Also returns "asd" correctly!
console.log(bfunc())
You need to explicitely bind the this value if you want this behaviour.
var c = new MyClass('asd'),
b = c.myfunc.bind(c);
console.log(b());
By default, this will point to the leftSide.ofTheDot(); in an invocation, or simply the object on which the function was called.
Note: Calling b(); is the same as window.b();.
Binding every function to the object instance is possible but rather inefficient because functions will not get shared across instances anymore.
E.g.
function MyClass(someVal) {
var me = this;
me.someVal = someVal;
me.someFn = function () {
return me.someVal;
};
}
The line var b... is a function reference and you're not actually calling the function.
Here you are assigning to a variable a result of myfunc() function.
var a = new MyClass('asd').myfunc();
And here, you are asigning to b variable function reference, rather then runing it, and assign result to variable.
var b = new MyClass('asd').myfunc;
And finaly here:
console.log(b())
You trying to log result of function b and in that case b() isn't defined anywhere, only it is assigned reference to function.
Try this:
console.log(b); // It logs [Function]
But besides that, your question is hard to understand.
P.S. Use semicolons!
When I use var keyword to declare any variable it gets declared inside the enclosing scope. However in the code below, I have declared function c (inside an object method a.b) with var keyword and still this inside the function c is bound to the global object window. Why is this?
var a = {
b: function () {
var c = function () {
return this;
};
return c();
}
};
document.write(a.b()); //prints: [object Window]
The value of this is determined by context, not scope.
When you call a function without any context (context.func()) as you do there (c()), the default context is the default object (which is window in browsers) unless you are in strict mode (in which case it is undefined instead).
(There are exceptions to this rule, such as apply, call, bind, and new but none of them apply here).
Many people get confused by this. The value this depends on one of 4 methods of invocation.
However, functional invocation and method-invocation cause most of the confusion.
If a function is a member of an object, this is the object itself.
obj.someFunction(); //method invocation
If a function is called without context this is the global object (in 'strict mode' this is undefined.)
someFunction(); //functional invocation
The confusion occurs when a function is called within an object, but not as a member of the object as in anObject.testWithHelper(..);
var testForThis = function(isThis, message) {
//this can be confusing
if(this === isThis)
console.log("this is " + message);
else
console.log("this is NOT " + message);
};
//functional invocation
testForThis(this, "global"); //this is global
var anObject = {
test: testForThis, //I am a method
testWithHelper: function(isThis, message) {
//functional invocation
testForThis(isThis, message + " from helper");
}
};
//method invocation
anObject.test(anObject, "anObject"); //this is anObject
//method invocation followed by functional invocation
anObject.testWithHelper(anObject, "an object"); //this is NOT anObject from helper
Here is my JSFIDDLE
If you would like c to return a, you can use closure:
var a = {
b: function () {
var that = this;
var c = function () {
return that;
};
return c();
}
};
Or avoid this all together:
var getNewA = function() {
var newA = {};
newA.b = function() {
var c = function() {
return newA;
};
return c();
};
return newA;
};
var newA = getNewA();
In javascript I have an option for log output that allows an element/selector to be used to specify where to output log/error messages. The output is formatted upon initialization like so:
var $messageOutput = options.messageOutputElement ? $(options.messageOutputElement) : null;
and it is used later on through a log function:
function outputMessage(msg)
{
if ($messageOutput !== null)
{
messageNum++;
var $messageOutput = $(options.messageOutputElement);
var html = $messageOutput.html();
html += '<b>' + messageNum + '</b><br/>' + msg + '<br/>';
$messageOutput.html(html);
$messageOutput.scrollTop($messageOutput[0].scrollHeight);
}
}
The problem is that even when $messageOutput === null, the if statement inside outputMessage() falls through. I've verified with Chome's debugger that $messageOutput indeed equals null. And indeed it steps into the if statement anyway.
Can anyone give me any insight as to why this might be happening? Thanks in advance for any input on this matter.
By declaring "var $messageOutput" in the scope of a function, you stop referencing the variable in the global scope. So, instead of referring to that, it refers to a variable that is yet to be set within the function body, which is undefined, not null. You should probably remove the "var" part of "var $messageOutput = $(options.messageOutputElement);", which will instead make the function refer to the $messageOutput outside the function.
To elaborate a bit:
"var" creates a new variable within the scope you are in, and to add to the confusion something like "var foo = function" behaves differently than "function foo". Here are a few fun cases & explanations for your reading pleasure:
var x = 5;
function f () {
console.log(x):
if (!x) {
var x = 22;
}
return x;
}
function g () {
console.log(x):
if (!x) {
x = 22;
}
return x;
}
f(); // prints nothing, and returns 22
g(); // prints 5, and returns the same value
What you also sometimes have to watch out for is that you can reference a function before declaration outside of the global scope, depending on how it's declared. For example:
function f () {
console.log(f_sub);
function f_sub () {
return "subfunction of f";
}
return f_sub;
}
function g() {
console.log(g_sub);
var g_sub = function () {
return "subfunction of g";
}
return g_sub;
}
f(); // prints and returns f_sub
g(); // prints undefined and returns g_sub
A conventional function declaration can be made anywhere and referenced anywhere, but assigning a function to a variable means it can't be referenced until the line has been executed.