es6 template string + short object literal transpilation failed [gulp, babel] - javascript

I've got an extremely small ES6 code:
var name = "MyName";
var version = "1.2.3";
var theThing = {
name,
version,
run: () => {
console.log(`You're using ${this.name}#${this.version}`);
}
};
theThing.run();
When I run it inside the browser console (chrome 53), I get the expected result: You're using MyName#1.2.3 is logged to the console. Both template strings work along with shortened object literal syntax.
However, when I try to use gulp/babel to transpile this code down to ES5, using the most basic setup (taken from here):
const gulp = require('gulp');
const babel = require('gulp-babel');
gulp.task('default', () => {
return gulp.src('src/app.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('dist'));
});
I get following output:
"use strict";
var name = "MyName";
var version = "1.2.3";
var theThing = {
name: name,
version: version,
run: function run() {
console.log("You're using " + undefined.name + "#" + undefined.version);
}
};
theThing.run();
As you can see, it's calling undefined.name instead of this.name, I've got absolutely no idea why is this replaced with undefined. Of course, this code doesn't work as expected:
VM329:8 Uncaught TypeError: Cannot read property 'name' of undefined(…)
and is not ES6 compliant (original implementation of ES6 in chrome53 works correctly).
You can also see this issue in the Babel REPL.
Am I doing something wrong - or is it a bug in babel?

in your original source try to replace
var theThing = {
name,
version,
run: () => {
console.log(`You're using ${this.name}#${this.version}`);
}
};
by
var theThing = {
name,
version,
run: function () {
console.log(`You're using ${this.name}#${this.version}`);
}
};
The arrow syntax function changes the way this works

Arrow functions don't bind to this (see here). Therefore when you define the function, the object doesn't yet exist... so Babel doesn't know about the context.
Getting rid of the arrow function will fix it.

The arrow function this points to window because of the lexical binding. When you call theThing.run(), you actually get window.name, and window.version in the template. If you rename the the global variables it won't work:
var n = "MyName";
var v = "1.2.3";
var theThing = {
name: n,
version: v,
run: () => {
console.log(`You're using ${this.name}#${this.version}`);
}
};
theThing.run();
To solve that, don't use the arrow function:
var n = "MyName";
var v = "1.2.3";
var theThing = {
name: n,
version: v,
run() {
console.log(`You're using ${this.name}#${this.version}`);
}
};
theThing.run();

The proper answer consists of 2 points:
arrow functions and classical ES5 functions have completely different this binding. this in classical functions is very dynamic (determined in runtime, no matter where is it defined), whereas in arrows, it's lexically bound (bound in where it's placed, runtime has no affect on that, runtime is already too late to change it). Lexical scope makes an arrow function borrow this from the closest enclosing classical function, if present. If not present, it's taken from global scope...
if not in strict mode, window is taken. If in strict mode, undefined is taken
... which precisely answers above question.

Related

How to force declaring function constructor properties in JavaScript (ES5) before their use?

This question may contain answer, I was thinking about possible solutions to that. However, I would like to know, if (and how) someone had done it in better way.
The problem is, that I have really long function constructors and the whole instance of that function contains lot of properties and so I try to 'initialize' these properties on the very beggining of the function constructor, because I would like to exactly know, what is used and assure myself, that there are no new declaration of properties somewhere in the middle of the code.
I know, that it is quite simple, when we think about 'normal' variables, when we "use strict":
"use strict"
favouriteAnimal = 'honeybadger'; //Uncaught ReferenceError: favouriteAnimal is not defined
This will throw and Error, because we haven't used:
"use strict"
const favouriteAnimal = 'porcupine';
or
"use strict"
var favouriteAnimal = null //or let
favouriteAnimal = 'aardvark';
In these following scenarios, please notice the 'this.importantFunction' and 'this.favouriteAnimal'. The rest of constructor function only demonstrates slightly complicated object structure, which would benefit from such early variable (property) definition.
But the struggle beggins within Objects, in following scenario, this is what is typically done, it is working, but it does not offer such safety precautions as to force us defining all variables first.
function TypicalContructorFunction1 ()
{
this.someRandomFunctionWithoutRealPurpose = function () {
console.warn('someone really used me?');
}
this.someStuff = 'coffee break';
this.differentStuff = 'broken coffee';
this.importantFunction = function (animalName) {
this.favouriteAnimal = animalName;
};
this.importantFunction('sewer rat');
return this;
};
var typicalObject1 = new TypicalContructorFunction1();
console.log(typicalObject1.favouriteAnimal)
Console will log an Object with property 'favouriteAnimal', but as the object structure is slightly fatter and so it is not clearly visible, that such property was defined.
Another approach with 'helping' function:
function useProperty (object, propertyName, closure) {
if (object && typeof object[propertyName] !== 'undefined') {
closure(object[propertyName], object, propertyName);
} else {
throw new ReferenceError('property "' + propertyName + '" not declared');
}
};
const TypicalContructorFunction2 = function ()
{
this.someRandomFunctionWithoutRealPurpose = function () {
console.warn('someone really used me?');
}
this.someStuff = 'coffee break';
this.differentStuff = 'broken coffee';
this.importantFunction = function (animalName) {
useProperty(this, 'favouriteAnimal', function (property)
{
property = animalName;
});
};
this.importantFunction('drosophila melanogaster');
return this;
};
var typicalObject2 = new TypicalContructorFunction2();
There is no need for console.log, because new ReferenceError will be thrown and execution stopped, which is what I want. However, this calling a function, passing a function to just change a simple value seems a bit as overkilling and I think, that it may have performance drawbacks too.
And as last (but not least) I came up with sealing the object after declaring all properties, so there cannot be added any other properties. (needs "use strict")
"use strict"
function TypicalContructorFunction3 ()
{
this.someStuff = null;
this.differentStuff = null;
this.someRandomFunctionWithoutRealPurpose = null;
this.importantFunction = null;
Object.seal(this);
this.someRandomFunctionWithoutRealPurpose = function () {
console.warn('someone really used me?');
}
this.someStuff = 'coffee break';
this.differentStuff = 'broken coffee';
this.importantFunction = function (animalName) {
this.favouriteAnimal = animalName;
};
this.importantFunction('jerboa');
return this;
};
var test = new TypicalContructorFunction3();
ReferenceError: property "favouriteAnimal" not declared
I think, that this may be the answer, while it does not seal the object deeply (there would definitely be some code snippets with deep sealing on StackOverflow).
I also know, that I can still use var/const/let inside function, but such variables become private and cannot be accessed from outside of the function.
Have you come up with different solutions to this issue?
PS: Please, this answer is asked for ECMAScript v5, as it is in the title.
Thanks for ideas :)

Arrow function vs function keyword [duplicate]

I am trying to access this inside my arrow function:
import myObject from '../myObjectPath';
export const myClass = Fluxxor.createStore({
initialize() {
this.list = [];
this.id = null;
},
myOutsideFunction(variable1) {
// here this in NOT undefined
myObject.getMyList(this.id, (myList) => {
// here this in undefined
this.list = myList;
}
});
)};
But inside arrow function which in ma callback function this is undefined!!
I am using babel to transpile the code:
myOutsideFunction: function myOutsideFunction() {
var _this = this;
myObject.getMyList(function (myList) {
_this.list = myList;
});
},
If this is undefined within an arrow function, it's undefined outside of the arrow as well. Arrow function simply capture the this of the surrounding scope.
In this case, you're declaring myOutsideFunction as a method on an object literal and never binding it or doing anything else that would call it with the object as this.
When debugging, bear in mind that transpilers can rename variables (and have to rename this for it to capture correctly). Using the original name in the console without sourcemaps that include renaming will show you undefined even if the original value isn't. Make sure you use the transpiled name in watches or console commands.

Using Lodash with create-react-app results in "Uncaught TypeError: _this.reduce is not a function"

I'm new to React and create-react-app and I'm attempting to use Lodash in my App.js file and I'm running into an error. Uncaught TypeError: _this.reduce is not a function. I've added
import _ from 'lodash';
import shuffle from 'lodash/shuffle';
import random from 'lodash/random';
import find from 'lodash/find';
to the top of my App.js and
import Lodash from 'lodash';
in my index.js file.
For testing I've used this reduce example from MDN, which works:
var total = [0, 1, 2, 3].reduce(function(sum, value) {
return sum + value;
}, 0);
But the line that uses lodash throws the error above:
var books = _.shuffle(this.reduce((p, c, i) => {
return p.concat(c.books);
}, [])).slice(0, 4);
In this case this is an array like this:
var data = [
{
name: 'Mark Twain',
imageUrl: 'images/authors/marktwain.jpg',
books: ['The Adventures of Huckleberry Finn']
}
];
As per the comments section, your this reference is not pointing to what you expect.
Change it to data and it should work.
Looking at your code its very unlikely that the keyword this actually refers to the array. Near impossible I would say. You could maybe write a whole book on how the this keyword behaves in Javascript. The _this value is a how babel handles the different behaviors of this.
Consider this example:
console.log(this)
function someFunction(){
console.log(this);
const someSubFunction = function() {
console.log(this)
}
someSubFunction();
const someOtherFunction = () => {
console.log(this)
}
someOtherFunction();
}
someFunction();
This code is transpiled by babel to:
"use strict";
console.log(undefined);
function someFunction() {
var _this = this;
console.log(this);
var someSubFunction = function someSubFunction() {
console.log(this);
};
someSubFunction();
var someOtherFunction = function someOtherFunction() {
console.log(_this);
};
someOtherFunction();
}
someFunction();
Notice how the this value is reassigned to a variable called _this.
In this example, all of the log statements print out undefined. If you use the keyword this at the root scope then it will (almost) certainly be undefined. In fact if you look at line 3 of the transpiled example, babel literally just replaces this with undefined. Inside a function on the global scope, this is also undefined.
Inside a class this refers to the instance of the class if you are directly inside a method defined by the class, or in the constructor.
Anyway long story short, you need figure out what this is actually referring to. Most likely you just need to assign your array to a variable and do:
var books = _.shuffle(data.reduce((p, c, i) => {
return p.concat(c.books);
}, [])).slice(0, 4);
If you are going to do lodash though, you could also be consistent and just use lodash like this:
var books = _.chain(data)
.reduce((p,c,i) => _.concat(c.books), [])
.shuffle()
.slice(0,4)
.value();
Slightly easier to read in my experience.

CommonJS Modules (with nodejs), strangeness

Okay, experimenting with CommonJS module system in the context of NodeJS.
module.exports = pricingCalculator;
function pricingCalculator (options) {
var target = {};
return target;
}
This works. Presumably the variable declaration of pricingCalculator is hoisted to the top of the function scope, so the misordering doesnt quite matter as the function is passed by reference anyway. I get that. What I dont understand is why the following two versions that work:
module.exports = pricingCalculator;
var pricingCalculator = function (options) {
var target = {};
return target;
}
Fail.
module.exports = pricingCalculator;
pricingCalculator = function (options) {
var target = {};
return target;
}
Fail. Curious to understand deeply what is going on.
In first example function is defined before assignment (java script way).
In second and third examples assignments are executed in sequence.
http://studiokoi.com/blog/article/execution_order_of_functions_and_variables_in_javascript_and_actionscript

Get JavaScript function-object from its name as a string?

In JavaScript, if I have a string in a variable, is there a way to get a reference to the function object which has that matching name? Note that jQuery is available to me so I can use any of its helper methods also.
For example:
myFunction = function(){};
var func_name = "myFunction";
var myFunctionPtr = ??? //how to get the function from the name
Thanks
if you know that its a global function you can use:
var functPtr = window[func_name];
//functPtr()
Otherwise replace window with the parent object containing the function.
I just did a quick test in Firebug and I was able to get the function from the name by simply eval()ing the name... I feel dirty using eval(), but it seems to get the job done here quite nicely.
var myFunctionPtr = eval(func_name);
This is never a preferred approach. Instead of keeping the function name in func_name, you could have kept the reference to a function just as successfully in something like func_to_call.
If you absolutely require to keep the function reference as a string, you would typically use a hash table to map an arbitrary name to a variable (JS has first-class functions which makes it possible)
myFunction = function(){};
var obj = {func_name: myFunction};
obj['func_name']();//executes the function
Fiddled (I don't know why, it's a such a tiny script :)
It was suggested that you use eval(func_name) - however this could quickly get out of control because of JS scoping.
You have also declared your myFunction = function(){}; as a global variable. On one hand, it lets you reference it as window[func_name] but on the other hand it pollutes the global scope.
this[func_name] should give you the function.
var myfunc = this[func_name];
myfunc();
It depends on where and how the function is (or isn't) declared.
If it's a global and not declared via let name = ... or const name = ... syntax (and it's not a class constructor declared with class), you can check by looking for it as a property on the global object. (Those caveats are all ES2015 things; more below.) You can get a reference to the global object via this in loose mode at global scope; browsers also give you a global called window. So assuming a browser:
if (typeof window[func_name] === "function") {
// ....
}
If it might not be a global, but rather is just in scope because your code closes over it, or if it was created using one of those ES2015 mechanisms I mentioned, there's really no good way to check other than eval:
if (eval("typeof " + func_name) === "function") {
// ....
}
Using eval is a last resort, and you must only use it with strictly-controlled input. But when you have to, and you have strictly-controlled input, it's fine.
About the ES2015 caveats:
The new let, const, and class are very interesting beasties: When used at global scope, they create globals, but they don't create properties on the global object. As of ES2015, although all properties of the global object are globals, not all globals are properties of the global object. It's all part of trying to rein in vastly-polluted global namespace and also bring greater security to the JavaScript binding model. (Now that we have true modules.)
So (note that this will only run in cutting-edge browsers):
// Global scope, in a browser (because I used `window` and `document.body`) that
// implements this aspect of ES2015 (as I write this, Firefox's SpiderMonkey
// doesn't, Chrome's V8 does on the latest Chrome; expect SpiderMonkey and IE
// to catch up pretty quick (didn't test IE Edge, maybe it's already there)
// Strict mode isn't required for this behavior, but for the moment V8 only
// supports the block-scoped constructs in strict mode.
"use strict";
let tbody = setup();
// Old-fashioned var: Creates a property on the global object, so
// we get "function, function"
var f1 = function() { /*...*/ };
result("var declaration", typeof f1, typeof window["f1"]);
// Function declaration: Creates a property on the global object, so
// "function, function"
function f2() {}
result("function declaration", typeof f2, typeof window["f2"]);
// `let` declaration: Doesn't create property on global object, so
// "function, undefined"
let f3 = function() { /*...*/ };
result("let declaration", typeof f3, typeof window["f3"]);
// `const` declaration: Doesn't create property on global object, so
// "function, undefined"
const f4 = function() { /*...*/ };
result("const declaration", typeof f4, typeof window["f4"]);
// `class` declaration: Doesn't create property on global object, so
// "function, undefined"
class C1 {}
result("class declaration", typeof C1, typeof window["C1"]);
function setup() {
document.body.insertAdjacentHTML(
"beforeend",
"<table>" +
"<thead>" +
"<tr><th>test</th><th>global</th><th>prop</th></tr>" +
"</thead>" +
"<tbody></tbody>" +
"</table>"
);
return document.body.querySelector("tbody");
}
function result(label, direct, win) {
tbody.insertAdjacentHTML(
"beforeend",
"<tr><td>" + [label, direct, win].join("</td><td>") + "</td></tr>"
);
}
body {
font-family: sans-serif;
}
table {
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 4px 8px;
}
Output on cutting-edge browsers:
+----------------------+------------+-----------+
| test | global | prop |
+----------------------+------------+-----------+
| var declaration | function | function |
| function declaration | function | function |
| let declaration | function | undefined |
| const declaration | function | undefined |
| class declaration | function | undefined |
+----------------------+------------+-----------+
Note: Some transpilers don't enforce this rigorously, so if you see different results in transpiled code, don't be surprised.
A safe way to do is to sandbox the alleged function while testing its type:
function isFunction(expr) {
function sandboxTemplate() {
var window, document, alert; // etc.
try {
return typeof $expr$ == "function";
} catch (e) {
return false;
}
}
try {
var sandbox = new Function(
sandboxTemplate.toString().replace("$expr$", expr)
+ "return sandboxTemplate()");
return sandbox();
} catch (e) {
return false;
}
}
function test(expr) {
document.write("<div>\"" + expr + "\" <b>is "
+ (isFunction(expr) ? "" : "not ")
+ "</b>a function</div>");
}
/* Let's do some testing */
function realFunction() {
}
test("realFunction"); // exists!
test("notHere"); // non-existent
test("alert('Malicious')"); // attempt to execute malicious code!
test("syntax error {"); // attempt to blow us up!
The output:
"realFunction" is a function
"notHere" is not a function
"alert('Malicious')" is not a function
"syntax error {" is not a function
The sandboxing code could be written in a more concise manner but I like using "template" functions instead of embedding JS code as string literals.
And oh, this does it nicely without using eval -- though one can argue that using a Function constructor is no different than an eval.
Use eval:
myFunction = function(){};
var func_name = "myFunction";
var myFunctionPtr = eval(func_name);
found the function and then call them
autoCallBack : function(_action){
$(".module").each(function(){
var modulName = $(this).attr("id");
if( isFunction(modulName) ){
eval(modulName)();
}
});
}
isFunction : function(_functionName){
try {
eval(_functionName);
} catch (error) {
return false;
}
return true;
}
For NodeJs
Write your functions in a separate file and export them and use with name reference of that to call them, Like
// functions.js
var funcOne = function(){
console.log('function ONE called')
}
module.exports={
// name_exported : internal_name
funcOne : funcOne
}
Use function defined in functions.js in index.js :
// index.js
var methods = require('./functions.js') // path to functions.js
methods['funcOne']()
OUTPUT :
> node index.js
> function ONE called

Categories