I have written this code
require 'watir'
b = Watir::Browser.new
b.window.maximize
b.goto 'URL'
# var headElement = document.getElementsByTagName(\"head\")[0];
sleep 3
b.execute_script("
const popperStylesheet = document.createElement('link');
popperStylesheet.rel = 'stylesheet';
popperStylesheet.href = 'https://unpkg.com/#popperjs/core#2.11.6/dist/umd/popper.css';
const tippyStylesheet = document.createElement('link');
tippyStylesheet.rel = 'stylesheet';
tippyStylesheet.href = 'https://unpkg.com/tippy.js#6.3.3/dist/tippy.css';
const popperScript = document.createElement('script');
popperScript.src = 'https://unpkg.com/#popperjs/core#2.11.6/dist/umd/popper.js';
const tippyScript = document.createElement('script');
tippyScript.src = 'https://unpkg.com/tippy.js#6.3.3/dist/tippy.umd.js';
tippyScript.onload = function() {
const customStyles = document.createElement('style');
customStyles.innerHTML = `
.custom-theme {
background-color: #1c00d4;
color: #ffffff;
border-radius: 10px;
}
.custom-theme .tippy-arrow {
color: #0012d4;
}
.tippy-box {
background-color: Green;
}
.tippy-content {
font-size: 20px;
}
`;
var inputElement = document.getElementById('UserName');
if (inputElement) {
console.log('Selected file:');
inputElement.addEventListener('focus', function() {
inputElement._tippy.show();
});
tippy(inputElement, {
content: 'Here you enter the first name, firstname should contain no numbers',
theme: 'custom-theme',
placement: 'right',
arrow: true,
duration: 300,
delay: [500, 0],
maxWidth: 200
});
}
var headElement = document.getElementsByTagName(\"head\")[0];
headElement.appendChild(popperStylesheet);
headElement.appendChild(tippyStylesheet);
headElement.appendChild(tippyScript);
headElement.appendChild(popperScript);
headElement.appendChild(customStyles);
};
var headElement = document.getElementsByTagName(\"head\")[0];
headElement.appendChild(popperStylesheet);
headElement.appendChild(tippyStylesheet);
headElement.appendChild(tippyScript);
headElement.appendChild(popperScript);
")
I don't have any way to check whether this tippy method is defined, it often throws this error
Uncaught ReferenceError: tippy is not defined
at tippyScript.onload (eval at executeScript (login.do:476:16), <anonymous>:47:5)
Is there any way can we check whether this method is defined correctly before we call it? I have even used sleep 3 as shown above before we execute it but still I am getting this error often.
Functions in JS are "first class objects". When you define a function f() {}, you're just creating a function (anonymous for a very little while) and storing its reference in a variable called f. You can check for the existence of f (and its type, etc) in the same way you would check for any other variable.
Example:
function f1() { console.log ("I am f1()!"); }
var f3 = f1; // Both variables will reference the same actual function
console.log ("f1 is a " + typeof f1);
console.log ("f2 is a " + typeof f2);
console.log ("f3 is a " + typeof f3);
if (typeof f1 != "undefined") f1();
if (typeof f2 != "undefined") f2();
if (typeof f3 != "undefined") f3();
Output:
f1 is a function
f2 is a undefined
f3 is a function
I am f1()!
I am f1()!
Related
Edit: I found the code that Stack Overflow uses: https://github.com/gh-canon/stack-snippet-console
I've found a bunch of answers that show how to output the console to a webpage, but I'm trying to make it so that the messages are also logged to the console. Ironically, if you run snippets on Stack Overflow, they do what I'm trying to.
// This causes a stack overflow
var native = window;
native.console = {
log: function(message){
$('ul.messages').append('<li>Log: ' + message + '</li>');
console.log(message);
},
error: function(message){
$('ul.messages').append('<li>Error: ' + message + '</li>');
console.error(message);
},
warn: function(message){
$('ul.messages').append('<li>Warn: ' + message + '</li>');
console.warn(message);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="messages"></ul>
I think you just need to cache the original console methods and call them from the cache-- the way you have it now calls your stubbed log, which causes infinite recursion (and thus a stackoverflow):
$(document).ready(function(){
console.log('You should know...');
console.error('Something went wrong...');
console.warn('Look out for this...');
})
// This should work
var native = window;
var originalConsole = native.console;
native.console = {
log: function(message){
$('ul.messages').append('<li>Log: ' + message + '</li>');
originalConsole.log(message);
},
error: function(message){
$('ul.messages').append('<li>Error: ' + message + '</li>');
originalConsole.error(message);
},
warn: function(message){
$('ul.messages').append('<li>Warn: ' + message + '</li>');
originalConsole.warn(message);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h3>messages</h3>
<ul class="messages"></ul>
You can create a wrapper function that takes in a function and outputs your modified function.
const wrapper = (fn, name) => {
return function(msg) {
$('ul.messages').append(`<li>${name}: ${msg}</li>`);
fn(msg);
};
}
$(document).ready(() => {
window.console.log = wrapper(console.log, "Log");
window.console.warn = wrapper(console.warn, "Warn");
window.console.error = wrapper(console.error, "Error");
});
I do not recommend you modify the original function, you can create a new one, and that can show the message on both console and your node.
Blow is pure javascript code.
Example 1
function decorator(wrap, func) {
return (...para) => {
func(para)
return wrap(para)
}
}
const mylog = decorator(window.console.log, (...para)=>{
const ul = document.querySelector(`ul[class=msg]`)
const range = document.createRange()
const frag = range.createContextualFragment(`<li>${para}</li>`)
ul.append(frag)
})
mylog("Hello world!")
<h3>messages</h3>
<ul class="msg"></ul>
Example 2
window.onload = () => {
const decoratorFunc = (methodName, msg) => {
const symbol = methodName === "info" ? "🐬" :
methodName === "error" ? "❌" :
methodName === "warn" ? "⚠" : ""
const ul = document.querySelector(`ul[class=msg]`)
const range = document.createRange()
const frag = range.createContextualFragment(`<li>${symbol} ${msg}</li>`)
ul.append(frag)
}
const myConsole = new MethodDecorator(window.console, decoratorFunc)
myConsole.Apply(["log", "info", "error", "warn", // those will run {decoratorFunc + ``window.console.xxx``}
"others" // 👈 I created, it doesn't belong method of window.console so it only run ``decoratorFunc``
])
myConsole.log("log test")
myConsole.info("info test")
console.info("not influenced") // not influenced
myConsole.error("error test")
myConsole.warn("warn test")
myConsole.others("others test")
myConsole.Reset()
// myConsole.warn("warn test") // error
}
class MethodDecorator {
constructor(obj, func) {
this.obj = obj
this.func = func
}
Apply(nameArray) {
const orgMethodNameArray = Object.getOwnPropertyNames(this.obj)
for (const methodName of nameArray) {
const orgMethodName = orgMethodNameArray.find(e => e === methodName) // if not found return undefined
const prop = {}
prop[methodName] = {
"configurable": true,
"value": (...args) => {
this.func(methodName, args) // decorator function
if (orgMethodName) {
this.obj[orgMethodName](args) // for example, console.log(args)
}
}
}
Object.defineProperties(this, prop)
}
}
Reset() {
const extraMethodNameArray = Object.getOwnPropertyNames(this).filter(name => name !== "obj" || name !== "func")
for (const extraMethodName of extraMethodNameArray) {
const prop = {}
prop[extraMethodName] = {
value: undefined
}
Object.defineProperties(this, prop)
}
}
}
<h3>messages</h3>
<ul class="msg"></ul>
👆 The console of Run code snippet will block some contents. Click the full page after you click run code snippet to see all contents.
Reference
window.console
I think I'm trying to call a Promise within a Promise (maybe even within another Promise). In the past I try to simplify my question and I end up getting more questions, so there's a lot below:
I have the following code as a module named myModule:
let https = require('https');
module.exports.getApiOne = function(value) {
var params = {..., path = '/api/getOne/' + value, ...};
return getApi(params).then(response => response);
};
module.exports.getApiTwo = function(value) {
var params = {..., path = '/api/getTwo/' + value, ...};
return getApi(params).then(response => response);
};
function getApi(params) {
return new Promise(function(resolve, reject) {
var req = https.request(params, function(res) {
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = Buffer.concat(body).toString();
} catch (e) {
reject(e);
}
resolve(body);
});
});
req.on('error', function(err) {
reject(err);
});
req.end();
});
}
On another file I have:
const status = require('myModule');
var someObject = {};
function someFunction(inputObject) {
// initialize object
if (!someObject[inputObject.Id]) {
someObject[inputObject.Id] = {};
someObject[inputObject.Id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.Id];
switch (inputObject.someVal) {
case 'test':
//... some code
objectForThis.stage = 'test';
break;
case 'hello':
status.getApiOne('world').then(response => {
console.log(response);
objectForThis.stage = 'zero'
});
break;
default:
someOtherFunction(objectForThis.stage).then(response => {
objectForThis.stage = response;
});
break;
}
someObject[inputObject.Id] = objectForThis;
}
function someOtherFunction(stage) {
var newStage;
new Promise(function(resolve, reject) {
switch (stage) {
case 'zero':
// some code
newStage = 'one';
case 'one':
status.getApiTwo('foo').then(response => {
console.log(response);
newStage = 'two';
/********************************************
I assume, the problem lies here, it's
'resolving' (below) before this sets the new
value for 'newStage'
********************************************/
});
break;
default:
// do nothing
break;
}
});
resolve(newStage);
}
When I call
someFunction({id = 1, someValue = 'test'}); // sets 'stage' to 'test'
someFunction({id = 1, someValue = 'hello'}); // sets 'stage' to 'zero'
someFunction({id = 1, someValue = 'foo'}); // sets 'stage' to 'one'
someFunction({id = 1, someValue = 'bar'}); // NOT setting 'stage' to 'two'
The reason for why is because Promises are asynchronous:
logOut("start of file");
new Promise(function(accept){
accept();
}).then(function(){
logOut("inside promise");
});
function makePromise(name) {
new Promise(function(accept){
accept();
}).then(function(){
logOut("inside promise inside makePromise " + name);
});
};
logOut("value returned from makePromise: " + makePromise("one"));
try {
// just to prove this
makePromise("two").then(function(accept){
accept();
}).then(function(){
logOut("after makePromise");
});
} catch(err) {
logOut("Failed to `.then` the return value from makePromise because:\n" + err.message);
}
logOut("end of file");
var outputList;
function logOut(str){
outputList = outputList || document.getElementById("output");
outputList.insertAdjacentHTML("beforeend", "<li><pre>" + str + "</pre></li>");
}
<ol id="output"></ol>
As seen above, the whole program does not pause for the .then statement. That is why they are called Promises: because the rest of the code goes on while the Promise is waiting to be resolved. Further, as seen above, there can be a value returned from a function only if the function explicitly returns the value via the then keyword. JavaScript functions do not automatically return the value of the last executed statement.
Please see my website here for more info on Promises.
In the demo below, I have attempted to fix up the fragments of the files you slapped on this question. Then, I proceeded to wrap them together into a quick single-file system I typed up
(function(){"use strict";
// NOTE: This setup code makes no attempt to accurately replicate the
// NodeJS api. This setup code only tries to concisely mimics
// the NodeJS API solely for the purposes of preserving your code
// in its present NodeJS form.
var modules = {}, require = function(fileName){return modules[fileName]};
for (var i=0; i < arguments.length; i=i+1|0)
arguments[i]({exports: modules[arguments[i].name] = {}}, require);
})(function https(module, require){"use strict";
////////////// https.js //////////////
module.exports.request = function(options, withWrapper) {
var p, when = {}, wrapper = {on: function(name, handle){
when[name] = handle;
}, end: setTimeout.bind(null, function(){
if (p === "/api/getOne/world") when.data("HTTP bar in one 1");
else if (p === "/api/getTwo/foo") when.data("HTTP foo in two 2");
else {console.error("Not stored path: '" + p + "'");
return setTimeout(when.error);}
setTimeout(when.end); // setTimeout used for asynchrony
}, 10 + Math.random()*30)}; // simulate random http delay
setTimeout(withWrapper, 0, wrapper); // almost setImmediate
return p = options.path, options = null, wrapper;
};
/******* IGNORE ALL CODE ABOVE THIS LINE *******/
}, function myModule(module, require) {"use strict";
////////////// myModule.js //////////////
const HttpsModule = require('https');
module.exports.getApiOne = function(value) {
var params = {path: '/api/getOne/' + value};
// There is no reason for `.then(response => response);`!
// It does absolutely nothing.
return getApi(params); // .then(response => response);
};
module.exports.getApiTwo = function(value) {
var params = {path: '/api/getTwo/' + value};
return getApi(params); // .then(response => response);
};
function getApi(params) {
return new Promise(function(resolve, reject) {
var req = HttpsModule.request(params, function(res) {
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = body.join("");//Buffer.concat(body).toString();
} catch (e) {
reject(e);
}
resolve(body);
});
});
req.on('error', function(err) {
reject(err);
});
req.end();
});
}
}, function main(module, require) {"use strict";
////////////// top JS script //////////////
const MyStatusModule = require('myModule');
const isPromise = isPrototypeOf.bind(Promise.prototype)
var someObject = {};
function someFunction(inputObject) {
// initialize object
// NOTE: Javascript IS case-sensitive, so `.Id` !== `.id`
if (!someObject.hasOwnProperty(inputObject.id)) {
someObject[inputObject.id] = {};
someObject[inputObject.id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.id];
switch (inputObject.someValue) {
case 'test':
//... some code
return objectForThis.stage = 'test';
break;
case 'hello':
return MyStatusModule.getApiOne('world').then(function (response) {
// console.log(response);
return objectForThis.stage = 'zero'
});
break;
default:
return someOtherFunction(objectForThis.stage).then(function (response) {
return objectForThis.stage = response;
});
break;
}
}
function someOtherFunction(stage) {
var newStage;
// If you return nothing, then you would be calling `.then` on
// on `undefined` (`undefined` is the default return value).
// This would throw an error.
return new Promise(function(resolve, reject) {
switch (stage) {
case 'zero':
// some code
newStage = 'one';
resolve(newStage); // you must call `resolve`
case 'one':
return MyStatusModule.getApiTwo('foo').then(function (response) {
// console.log(response);
newStage = 'two';
/********************************************
I assume, the problem lies here, it's
'resolving' (below) before this sets the new
value for 'newStage'
********************************************/
resolve(newStage); // you must call `resolve`
});
break;
default:
// do nothing
resolve(newStage); // you must call `resolve`
break;
}
});
}
// tests:
function logPromise(){
var a=arguments, input = a[a.length-1|0];
if (isPromise(input)) {
for (var c=[null], i=0; i<(a.length-1|0); i=i+1|0) c.push(a[i]);
return input.then(logPromise.bind.apply(logPromise, c));
} else console.log.apply(console, arguments);
}
logPromise("test->test: ", someFunction({id: 1, someValue: 'test'})); // sets 'stage' to 'test'
logPromise("hello->zero: ", someFunction({id: 1, someValue: 'hello'})) // sets 'stage' to 'zero'
.finally(function(){ // `.finally` is like `.then` without arguments
// This `.finally` waits until the HTTP request is done
logPromise("foo->one: ", someFunction({id: 1, someValue: 'foo'})) // sets 'stage' to 'one'
.finally(function(){
debugger;
logPromise("bar->two: ", someFunction({id: 1, someValue: 'bar'})); // NOT setting 'stage' to 'two'
});
});
});
If it is not apparent already, do not copy the above snippet into your code. It will break your code because the above snippet is rigged with dummy Node modules designed to produce set results. Instead, copy each individual file (each wrapped in a function) from the snippet above into the corresponding file of your code if you must copy. Also, while copying, keep in mind not to copy the dummy stuff above the blatant IGNORE ALL CODE ABOVE THIS LINE indicator. Also, do not forget to test rigorously. I am much more familiar with browser JavaScript than Node JavaScript, so it is possible (though unlikely) that I may have introduced a potential source of errors.
someObject[inputObject.id] = objectForThis; is not needed
I could give you a super concise quick answer to this question. However, I feel that the "quick" answer would not do you justice because this particular question requires a far greater depth of understanding to be able come up with an explanation than to simply read someone else's explanation. Further, this is a very crucial concept in Javascript. Thus, it is very necessary to be able to come up with the explanation on your own so that you do not run into the same trouble again 5 minutes from now. Thus I have written the below tutorial to guide you to the answer so that you can be sure that you have complete understanding.
In Javascript, there are expressions such as 7 - 4 which yields 3. Each expression returns a value that may be used by further expressions. 3 * (4 + 1) first evaluates 4 + 1 into 3 * 5, then that yields 15. Assignment expressions (=,+=,-=,*=,/=,%=,**=,&=,|=, and ^=) only change the left-hand variable. Any right-hand variable stays the exact same and contains the same value:
var p = {};
var ptr = p;
// Checkpoint A:
console.log('Checkpoint A: p === ptr is now ' + (p === ptr));
ptr = null;
// Checkpoint B:
console.log('Checkpoint B: p === ptr is now ' + (p === ptr));
Let us examine what p and ptr look like at Checkpoint A.
As seen in the above diagram, both p and ptr are kept separate even though they both point to the same object. Thus, at Checkpoint B, changing ptr to a different value does not change p to a different value. At Checkpoint B, variable p has remained unchanged while ptr is now null.
At Checkpoint A, keep in mind that (although p and ptr are distinct variables with different memory addresses) p and ptr both point to the same object because the location in memory they point to is the same index number. Thus, if we changed this object at checkpoint A, then reading/writing the properties of p would be the same as reading/writing the properties of ptr because you are changing the data being pointed to, not which variable is pointing to what data.
function visualize(inputObject) {
// displays an object as a human-readable JSON string
return JSON.stringify(inputObject);
}
var p = {};
p.hello = "world";
// Checkpoint 0:
console.log('Checkpoint 0: p is ' + visualize(p));
var ptr = p;
ptr.foo = "bar";
// Checkpoint A:
console.log('Checkpoint A: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr));
ptr = null;
// Checkpoint B:
console.log('Checkpoint B: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr));
p.foo = p.hello;
// Checkpoint C:
console.log('Checkpoint C: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr));
As seen at Checkpoint A above, changing p is the same as changing ptr. What about when we reassign an object? The old object which is no longer pointed to get cleaned up by tan automatic Garbage Collector.
function visualize(inputObject) {
// displays an object as a human-readable JSON string
return JSON.stringify(inputObject);
}
var first = {one: "is first"};
first = {uno: "es el primero"};
var second = {two: "is second"};
second = first;
console.log("second is " + JSON.stringify(second));
Function arguments are the same as variables in this respect.
var obj = {};
setVariable(obj);
obj.key = "value";
console.log("obj is " + JSON.stringify(obj));
function setVariable(inputVariable){
inputVariable.setValue = "set variable value";
inputVariable = null;
}
Is the same as:
var obj = {};
/*function setVariable(*/ var inputVariable = obj; /*){*/
inputVariable.setValue = "set variable value";
inputVariable = null;
/*}*/
obj.key = "value";
console.log("obj is " + JSON.stringify(obj));
Objects are also no different:
function visualize(inputObject) {
// displays an object as a human-readable JSON string
return JSON.stringify(inputObject);
}
var variables = {};
var aliasVars = variables;
// Now, `variables` points to the same object as `aliasVars`
variables.p = {};
aliasVars.p.hello = "world";
// Checkpoint 0:
console.log('Checkpoint 0: variables are ' + visualize(variables));
console.log('Checkpoint 0: aliasVars are ' + visualize(aliasVars));
variables.ptr = aliasVars.p;
aliasVars.ptr.foo = "bar";
// Checkpoint A:
console.log('Checkpoint A: variables are ' + visualize(variables));
console.log('Checkpoint A: aliasVars are ' + visualize(aliasVars));
variables.ptr = null;
// Checkpoint B:
console.log('Checkpoint B: variables are ' + visualize(variables));
console.log('Checkpoint B: aliasVars are ' + visualize(aliasVars));
aliasVars.p.foo = variables.p.hello;
// Checkpoint C:
console.log('Checkpoint C: variables are ' + visualize(variables));
console.log('Checkpoint C: aliasVars are ' + visualize(aliasVars));
Next is object notation just to make sure that we are on the same page.
var obj = {};
obj.one = 1;
obj.two = 2;
console.log( "obj is " + JSON.stringify(obj) );
is the same as
var obj = {one: 1, two: 2};
console.log( "obj is " + JSON.stringify(obj) );
is the same as
console.log( "obj is " + JSON.stringify({one: 1, two: 2}) );
is the same as
console.log( "obj is {\"one\":1,\"two\":2}" );
The hasOwnProperty allows us to determine whether or not an object has a property.
var obj = {};
// Checkpoint A:
console.log("Checkpoint A: obj.hasOwnProperty(\"hello\") is " + obj.hasOwnProperty("hello"));
console.log("Checkpoint A: obj[\"hello\"] is " + obj["hello"]);
obj.hello = "world"; // now set the variable
// Checkpoint B:
console.log("Checkpoint B: obj.hasOwnProperty(\"hello\") is " + obj.hasOwnProperty("hello"));
console.log("Checkpoint B: obj[\"hello\"] is " + obj["hello"]);
obj.hello = undefined;
// Checkpoint C:
console.log("Checkpoint C: obj.hasOwnProperty(\"hello\") is " + obj.hasOwnProperty("hello"));
console.log("Checkpoint C: obj[\"hello\"] is " + obj["hello"]);
delete obj.hello;
// Checkpoint D:
console.log("Checkpoint D: obj.hasOwnProperty(\"hello\") is " + obj.hasOwnProperty("hello"));
console.log("Checkpoint D: obj[\"hello\"] is " + obj["hello"]);
Prototypes in javascript are crucial to understanding hasOwnProperty and work as follows: when a property is not found in an object, the object's __proto__ is checked for the object. When the object's __proto__ does not have the property, the object's __proto__'s __proto__ is checked for the property. Only after an object without a __proto__ is reached does the browser assume that the wanted property does not exist. The __proto__ is visualized below.
var obj = {};
console.log('A: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('A: obj.foo is ' + obj.foo);
obj.__proto__ = {
foo: 'value first'
};
console.log('B: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('B: obj.foo is ' + obj.foo);
obj.foo = 'value second';
console.log('C: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('C: obj.foo is ' + obj.foo);
delete obj.foo;
console.log('D: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('D: obj.foo is ' + obj.foo);
delete obj.__proto__.foo;
console.log('E: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('E: obj.foo is ' + obj.foo);
Infact, we could even store a reference to the __proto__ and share this reference between objects.
var dog = {noise: "barks"};
var cat = {noise: "meow"};
var mammal = {animal: true};
dog.__proto__ = mammal;
cat.__proto__ = mammal;
console.log("dog.noise is " + dog.noise);
console.log("dog.animal is " + dog.animal);
dog.wagsTail = true;
cat.clawsSofa = true;
mammal.domesticated = true;
console.log("dog.wagsTail is " + dog.wagsTail);
console.log("dog.clawsSofa is " + dog.clawsSofa);
console.log("dog.domesticated is " + dog.domesticated);
console.log("cat.wagsTail is " + cat.wagsTail);
console.log("cat.clawsSofa is " + cat.clawsSofa);
console.log("cat.domesticated is " + cat.domesticated);
However, the above syntax is terribly illperformant because it is bad to change the __proto__ after the object has been created. Thus, the solution is to set the __proto__ along with the creation of the object. This is called a constructor.
function Mammal(){}
// Notice how Mammal is a function, so you must do Mammal.prototype
Mammal.prototype.animal = true;
var dog = new Mammal();
// Notice how dog is an instance object of Mammal, so do NOT do dog.prototype
dog.noise = "bark";
var cat = new Mammal();
cat.noise = "meow";
console.log("dog.__proto__ is Mammal is " + (dog.__proto__===Mammal));
console.log("cat.__proto__ is Mammal is " + (cat.__proto__===Mammal));
console.log("dog.__proto__ is Mammal.prototype is " + (dog.__proto__===Mammal.prototype));
console.log("cat.__proto__ is Mammal.prototype is " + (cat.__proto__===Mammal.prototype));
console.log("dog.noise is " + dog.noise);
console.log("dog.animal is " + dog.animal);
dog.wagsTail = true;
cat.clawsSofa = true;
Mammal.prototype.domesticated = true;
console.log("dog.wagsTail is " + dog.wagsTail);
console.log("dog.clawsSofa is " + dog.clawsSofa);
console.log("dog.domesticated is " + dog.domesticated);
console.log("cat.wagsTail is " + cat.wagsTail);
console.log("cat.clawsSofa is " + cat.clawsSofa);
console.log("cat.domesticated is " + cat.domesticated);
Next, the this object in Javascript is not a reference to the instance like in Java. Rather, the this in Javascript refers to the object in the expression object.property() when functions are called this way. When functions are not called this way, the this object refers to undefined in strict mode or window in loose mode.
"use strict"; // "use strict"; at the VERY top of the file ensures strict mode
function logThis(title){
console.log(title + "`this === undefined` as " + (this === undefined));
if (this !== undefined) console.log(title + "`this.example_random_name` as " + this.example_random_name);
}
logThis.example_random_name = "log this raw function";
logThis("logThis() has ");
var wrapper = {logThis: logThis, example_random_name: "wrapper around logThis"};
wrapper.logThis("wrapper.logThis has ");
var outer = {wrapper: wrapper, example_random_name: "outer wrap arounde"};
outer.wrapper.logThis("outer.wrapper.logThis has ");
We can finally take all this knowledge and extrapolate it out to the real example.
var someObject = {};
function someFunction(inputObject) {
if (!someObject.hasOwnProperty(inputObject.id)) {
someObject[inputObject.id] = {};
someObject[inputObject.id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.id];
objectForThis.stage = inputObject.stage;
}
var setTo = {};
setTo.id = 1;
setTo.stage = "first stage";
someFunction(setTo);
console.log("someObject is " + JSON.stringify(someObject));
First, let us inline the function and the setTo
var someObject = {};
var setTo = {id: 1, stage: "first stage"};
/*function someFunction(*/ var inputObject = setTo; /*) {*/
if (!someObject.hasOwnProperty(inputObject.id)) {
someObject[inputObject.id] = {};
someObject[inputObject.id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.id];
objectForThis.stage = inputObject.stage;
/*}*/
console.log("someObject is " + JSON.stringify(someObject));
Next, let us inline the setTo object.
var someObject = {};
var setTo = {id: 1, stage: "first stage"};
if (!someObject.hasOwnProperty(setTo.id)) {
someObject[setTo.id] = {};
someObject[setTo.id].contact = {};
}
// get object
var objectForThis = someObject[setTo.id];
objectForThis.stage = setTo.stage;
console.log("someObject is " + JSON.stringify(someObject));
Then:
var someObject = {};
if (!someObject.hasOwnProperty(1)) {
someObject[1] = {};
someObject[1].contact = {};
}
// get object
var objectForThis = someObject[1];
objectForThis.stage = "first stage";
console.log("someObject is " + JSON.stringify(someObject));
To visually demonstrate the pointers:
var someObject = {};
if (!someObject.hasOwnProperty(1)) {
var createdObject = {};
someObject[1] = createdObject;
someObject[1].contact = {};
}
// get object
var objectForThis = someObject[1];
console.log("createdObject === objectForThis is " + (createdObject === objectForThis));
objectForThis.stage = "first stage";
console.log("someObject is " + JSON.stringify(someObject));
Please tell me if you have any further questions.
If you are just glancing at this, then please don't forget to read the full article above. I promise you that my article above is shorter and tries to go deeper into Javascript than any other article you may find elsewhere on the internet.
No, your someOtherFunction should not use new Promise. You should just chain onto the status.getApiTwo('foo') call, or create immediately resolved promises using Promise.resolve. That way it will always return a promise like your call in someFunction expects it.
function someOtherFunction(stage) {
switch (stage) {
case 'zero':
// some code
return Promise.resolve('one');
case 'one':
return status.getApiTwo('foo').then(response => {
// ^^^^^^
console.log(response);
return 'two';
// ^^^^^^
});
default:
// do nothing
return Promise.resolve(undefined);
}
}
Alternatively you could use async/await:
async function someOtherFunction(stage) {
switch (stage) {
case 'zero':
// some code
return 'one';
case 'one':
const response = await status.getApiTwo('foo');
console.log(response);
return 'two';
default:
// do nothing
break;
}
}
I want to reuse the function sayMyName but with different variables. Please let me know if I'm structuring this the wrong way and what is the best practice what what I'm trying to do.
var sayMyName = function(myName) {
console.log(myName)
};
var name1 = function() {
// myName should not be a global variable
// because there may be more variables/functions
// that I'd want to closed inside sayMyName().
// Declaring all of them to the global scope is not ideal.
var myName = 'Walter';
sayMyName();
// I don't want to pass in myName as argument like this:
// sayMyName(myName);
// I want myName to be implicitly included in sayMyName()
// I want to pass in everything that is declared in name1 to sayMyName() implicitly.
};
var name2 = function() {
var myName = 'White';
sayMyName();
}
name1(); // should give me 'Walter'
name2(); // should give me 'White'
I'm not sure why you specifically want a closure, but by looking at your example it seems that a bind would be more appropriate than a closure.
var sayMyName = function(myName) {
console.log(myName)
};
var name1 = sayMyName.bind(undefined, 'Walter');
var name2 = sayMyName.bind(undefined, 'White');
name1(); // log 'Walter'
name2(); // log 'White'
Move your variable myName to the outermost scope:
var myName;
var sayMyName = function() {
console.log(myName)
};
var name1 = function() {
myName = 'Walter';
sayMyName();
};
var name2 = function() {
myName = 'White';
sayMyName();
}
name1(); // should give me 'Walter'
name2(); // should give me 'White'
Update: Thinking about it, if you're willing to use the non-standard, Error.stack attribute, are willing to use named functions, and are willing to use a naming convention, you could hackishly achieve your goal:
function sayMyName() {
try {
throw new Error();
}
catch (e) {
if (e.stack) { // non-standard attribute
var reNames = /^\s*at myNameIs([A-Z][^(\s]*)\s*\(/mg;
reNames.lastIndex = 0;
var buffer = [];
for (var match = reNames.exec(e.stack); null !== match; match = reNames.exec(e.stack)) {
buffer.push(match[1]);
}
console.log(buffer.join(" "));
}
}
}
function myNameIsWalter() {
sayMyName();
}
function myNameIsWhite() {
myNameIsWalter();
};
myNameIsWalter(); // "Walter"
myNameIsWhite(); // "Walter White"
... and if you're willing to use eval (bad !!!), then you could do fancier things like the following:
var sayMyName = function () {
try {
throw new Error();
}
catch (e) {
if (e.stack) { // non-standard attribute
var reNames = /^\s*at ([_a-zA-Z][_a-zA-Z0-9]+(\.[_a-zA-Z][_a-zA-Z0-9]+)*)/mg;
reNames.lastIndex = 0;
var reMyName = /\bmyName\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
var identifier, definition, match, myName, buffer = [];
while (null !== (match = reNames.exec(e.stack))) {
try {
identifier = match[1];
if ("sayMyName" !== identifier) {
definition = eval(match[1] + '.toString()');
if (/\bsayMyName\(\)/.test(definition)) {
reMyName.lastIndex = 0;
buffer.length = 0;
while (null !== (myName = reMyName.exec(definition))) {
buffer.push(myName[1]);
}
console.log(buffer.join(" "));
}
}
}
catch (_) {
// continue
}
}
}
}
};
function name1() {
var myName = "Walter";
sayMyName();
}
function name2() {
var myName;
myName = "Walter";
myName = "White";
sayMyName();
}
name1(); // "Walter"
name2(); // "Walter White"
You could even use the non-standard, Function.caller attribute, which is probably the cleanest approach (if it works in your browser -- it's non-standard for a reason):
function sayMyName() {
var reMyName = /\bmyName\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
var definition, buffer = [];
for (var caller = sayMyName.caller; caller; caller = caller.caller) {
definition = caller.toString();
if (/\bsayMyName\(\)/.test(definition)) {
reMyName.lastIndex = 0;
buffer.length = 0;
while (null !== (myName = reMyName.exec(definition))) {
buffer.push(myName[1]);
}
console.log(buffer.join(" "));
}
}
};
function name1() {
var myName = "Walter";
sayMyName();
}
function name2() {
var myName;
myName = "Walter";
myName = "White";
sayMyName();
}
name1(); // "Walter"
name2(); // "Walter White"
This is arguably no less code than just passing a parameter, though.
If you want closure then this is example.
function sayMyName(myName){
return function(){
console.log(myName); //myName is available from parent scope
return myName;
}
}
var name1 = sayMyName('Walter');
var name2 = sayMyName('White');
//no console output by now
name1(); //Walter
name2(); //White
//myName is not available in global scope
console.log(myName); //undefined
Here's my compose function, as a polyfill
Function.prototype.compose = function(prevFunc) {
var nextFunc = this;
return function() {
return nextFunc.call(this, prevFunc.apply(this,arguments));
}
}
These work:
function function1(a){return a + ' do function1 ';}
function function2(b){return b + ' do function2 ';}
function function3(c){return c + ' do function3 ';}
var myFunction = alert(function1).compose(function2).compose(function3);
myFunction('do');
var roundedSqrt = Math.round.compose(Math.sqrt)
roundedSqrt(6);
var squaredDate = alert.compose(roundedSqrt).compose(Date.parse)
quaredDate("January 1, 2014");
But this does not work!
var d = new Date();
var alertMonth = alert.compose(getMonth); <--
alertMonth(d); ^^^^
Error throws error "Uncaught ReferenceError: getMonth is not defined" in google chrome.
Now, if I try either of these instead:
var d = new Date();
function pluckMonth(dateObject) {return dateObject.getMonth();}
var alertMonth = alert.compose(pluckMonth);
var alertMonth2 = alert.compose(function(d){return d.getMonth()});
alertMonth(d);
alertMonth2(d);
They work.
Ok, so, why is that? I don't want to write extra functions, I want it to just work. The compose function uses the apply utility and just uses this for the thisArg, so it should work for object members as well as stand-alone functions, right??
i.e., these are equivalent
this.method()
method.call.apply(this)
jsFiddle: http://jsfiddle.net/kohq7zub/3/
If it comes to prototyping the Function object a whole bunch of libraries really do not acknowledge functions as methods.
The last entry in such arguments signatures always should be a target object, a method then can act upon. It solves the binding within the implementation; Thus avoiding to be forced later on having to use bind again and again as sole solution.
The given example slightly needs to be changed (jsFiddle) to ...
Function.prototype.compose = function(prevFunc, target) {
var nextFunc = this;
return function() {
return nextFunc.call(this, prevFunc.apply(target, arguments));
};
};
const d = (new Date);
let alertMonth;
// both variants do work.
alertMonth = alert.compose(d.getMonth, d);
alertMonth();
alertMonth = alert.compose(Date.prototype.getMonth, d);
alertMonth();
Within a next code iteration the above provided code then might result in something similar to the following implementation ...
const protoGetMonth = Date.prototype.getMonth;
let logMonth;
logMonth = ((month) => console.log(month))
.compose(protoGetMonth, (new Date));
logMonth();
logMonth = console.log
.compose(protoGetMonth, (new Date))
.bind(console);
logMonth();
let alertMonth = alert.compose(protoGetMonth, (new Date));
alertMonth();
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function (function_prototype) {
var
isFunction = (function (TYPEOF_FUNCTION_TYPE) {
return function (type) {
return (
(typeof type == TYPEOF_FUNCTION_TYPE)
&& (typeof type.call == TYPEOF_FUNCTION_TYPE)
&& (typeof type.apply == TYPEOF_FUNCTION_TYPE)
);
}
}(typeof function_prototype)),
getSanitizedTarget = function (target) {
return (target == null) ? null : target;
}
;
function_prototype.compose = function (previous, target) { // compose
var
previousTarget = getSanitizedTarget(target),
proceed = this
;
return (isFunction(previous) && isFunction(proceed) && function () {
return proceed.call(this, previous.apply(previousTarget, arguments));
}) || proceed;
};
}(Function.prototype));
</script>
Dart, Google's new web language, says it supports outputting to JavaScript.
What does a simple conversion look like?
main() {
print('Hello, Dart!');
}
When compiled with dart2js (as of 2013-04-26) (see note at bottom) it is converted into:
// Generated by dart2js, the Dart to JavaScript compiler.
// The code supports the following hooks:
// dartPrint(message) - if this function is defined it is called
// instead of the Dart [print] method.
// dartMainRunner(main) - if this function is defined, the Dart [main]
// method will not be invoked directly.
// Instead, a closure that will invoke [main] is
// passed to [dartMainRunner].
function Isolate() {}
init();
var $ = Isolate.$isolateProperties;
// Bound closures
$.Primitives_printString = function(string) {
if (typeof dartPrint == "function") {
dartPrint(string);
return;
}
if (typeof window == "object") {
if (typeof console == "object")
console.log(string);
return;
}
if (typeof print == "function") {
print(string);
return;
}
throw "Unable to print message: " + String(string);
};
$.main = function() {
$.Primitives_printString("Hello, Dart!");
};
$.String = {builtin$cls: "String"};
var $ = null;
Isolate = Isolate.$finishIsolateConstructor(Isolate);
var $ = new Isolate();
// BEGIN invoke [main].
if (typeof document !== "undefined" && document.readyState !== "complete") {
document.addEventListener("readystatechange", function () {
if (document.readyState == "complete") {
if (typeof dartMainRunner === "function") {
dartMainRunner(function() { $.main(); });
} else {
$.main();
}
}
}, false);
} else {
if (typeof dartMainRunner === "function") {
dartMainRunner(function() { $.main(); });
} else {
$.main();
}
}
// END invoke [main].
function init() {
Isolate.$isolateProperties = {};
Isolate.$finishIsolateConstructor = function(oldIsolate) {
var isolateProperties = oldIsolate.$isolateProperties;
isolateProperties.$currentScript = typeof document == "object" ? document.currentScript || document.scripts[document.scripts.length - 1] : null;
var isolatePrototype = oldIsolate.prototype;
var str = "{\n";
str += "var properties = Isolate.$isolateProperties;\n";
var hasOwnProperty = Object.prototype.hasOwnProperty;
for (var staticName in isolateProperties) {
if (hasOwnProperty.call(isolateProperties, staticName)) {
str += "this." + staticName + "= properties." + staticName + ";\n";
}
}
str += "}\n";
var newIsolate = new Function(str);
newIsolate.prototype = isolatePrototype;
isolatePrototype.constructor = newIsolate;
newIsolate.$isolateProperties = isolateProperties;
return newIsolate;
};
}
//# sourceMappingURL=out.js.map
Note for posterity: The original answer to this question has been modified to reflect the current state of affairs.
On 2012-05-12 the dart output for Hello World was 18,718 characters.
On 2012-08-29 the output was 1531 characters.
On 2013-04-26, the output was 2642 characters.
dart2js can minify code. Here is an example (as of 2013-04-26)
// Generated by dart2js, the Dart to JavaScript compiler.
function I(){}
init()
var $=I.p
$.ib=function(a){if(typeof dartPrint=="function"){dartPrint(a)
return}if(typeof window=="object"){if(typeof console=="object")console.log(a)
return}if(typeof print=="function"){print(a)
return}throw "Unable to print message: " + String(a)}
$.E2=function(){$.ib("Hello, Dart!")}
$.qU={builtin$cls:"qU"}
var $=null
I = I.$finishIsolateConstructor(I)
var $=new I()
if (typeof document !== "undefined" && document.readyState !== "complete") {
document.addEventListener("readystatechange", function () {
if (document.readyState == "complete") {
if (typeof dartMainRunner === "function") {
dartMainRunner(function() { $.E2(); });
} else {
$.E2();
}
}
}, false);
} else {
if (typeof dartMainRunner === "function") {
dartMainRunner(function() { $.E2(); });
} else {
$.E2();
}
}
function init(){I.p={}
I.$finishIsolateConstructor=function(a){var z=a.p
z.$currentScript=typeof document=="object"?document.currentScript||document.scripts[document.scripts.length-1]:null
var y=a.prototype
var x="{\n"
x+="var properties = I.p;\n"
var w=Object.prototype.hasOwnProperty
for(var v in z){if(w.call(z,v)){x+="this."+v+"= properties."+v+";\n"}}x+="}\n"
var u=new Function(x)
u.prototype=y
y.constructor=u
u.p=z
return u}}//# sourceMappingURL=out.js.map
On 2013-04-26, the minified code was 1386 characters.
The output of the Dart->JavaScript compiler is a moving target. The first release (technical preview) didn't do a lot of tree shaking and was thus pretty big.
The new (experimental) frog compiler is much better in this respect (David Chandler's blog), but I expect DartC to improve considerably too.