I'm filtering some messages with the following code:
const consoleLog = console.log;
console.log = (...args) => {
if (args.length === 0 || typeof args[0] !== 'string' || !args[0].includes('[HMR]')) {
consoleLog.apply(console, args);
}
};
How can I test the console output of this function? Normally, I'd just mock console.log, but in this case it's overwritten by the code above, so I can't do that.
Or don't mess with the original console.log at all.
const consoleLog = (...args) => {
if (args.length === 0 || typeof args[0] !== 'string' || !args[0].includes('[HMR]')) {
console.log.apply(console, args);
}
};
Then utilize consoleLog exclusively in your application code.
Benefit of this indirection:
It's safer. You never monkey-patch the original console.log at all.
It's more future-proof. If you switch from console.log to e.g. a 3rd-party logging framework later, you don't have to adjust all your logging code.
However, if you are intentionally configuring code beyond your control (3rd-party) that uses console.log, this of course will not work for that case.
With this approach, for unit testing, spy on console.log (which was never modified) as recommended in the answer from estus.
It's generally not a good practice to monkey-patch global APIs. And it's better to expose original function just in case, at least for testing purposes.
It would be easier to test it if it were more flexible, e.g. patched code could be controlled with environment variables, in case there's a need to fall back to original behaviour:
console._logOriginal = console.log;
console.log = (...args) => {
if (
!process.env.NO_HMR_SPAM ||
(args.length === 0 || typeof args[0] !== 'string' || !args[0].includes('[HMR]'))
) {
console._logOriginal(...args);
}
};
In this case console._logOriginal can be spied.
Otherwise original console.log should be backed up before this module is evaluated, it shouldn't be imported before that:
const consoleLogOriginal = console.log;
it('...', async () => {
const consoleLogSpy = jest.spyOn(console, 'log');
await import('moduleThatManglesConsole');
console.log('[HMR]');
expect(consoleLogSpy).not.toHaveBeenCalled();
console.log('');
expect(consoleLogSpy).toHaveBeenCalledWith('');
})
afterEach(() => {
console.log = consoleLogOriginal;
});
Related
I have to get a value of a inner component. Which is better 1 or 2 when compared ONLY for performance, this method is called several times. The foo may be null depending on the runtime, but foo may have a value but bar may be null etc. It is not an error case or unexpected case that any of foo, bar, innerfoo, innerbar are null, any of them can be null at the runtime depending on the hierarchy. But innerbar can not have any value of if foo is null so there is no need to check the inner components if foo is not defined.
//1
function getInnerValue() {
if (foo && foo.bar && foo.bar.innerfoo && foo.bar.innerfoo && foo.bar.innerfoo.innerbar) {
return foo.bar.innerfoo.innerbar.x;
}
return 0;
}
//2
function getInnerValue() {
try {
return foo.bar.innerfoo.innerbar.x;
}
catch (e) {
return 0;
}
}
Anything with a try catch that ever gets triggered will incur a large performance hit.
Personally I don't find the long chain of truthy checks any less readable than a try catch block. Just longer, ugly and verbose. But really, would anyone ever have trouble reading that (or editing it)? Or better put, would you even need to read it to understand what it is doing?
In fact if anything the try catch block feels like more of a hack. I still have to think about why I'm trying to catch an exception, and it's still ugly.
In any case, we now have optional chaining, so the above doesn't even really matter anymore. I'd go with something like return x?.y?.z?.a?.b?.c?.d||0.
You can of course go with guard functions, as you've probably already noticed reading the thread you've linked. Something like lodash _.get or your own guard(a,'b','c','d').
optional chaining browser compatibility
Babel transpiled output of optional chaining
x?.y will immediately return undefined if x is null or undefined
Browser compatibility is relatively new. Check table of compatibility. You should add Babel transpilation to your pipeline.
optional chaining without transpilation:
foo = {}
function fn() {
if (foo?.bar?.innerfoo?.innerbar)
return foo.bar.innerfoo.innerbar.x
return 0
}
console.log(fn())
foo = {bar: { innerfoo: { innerbar: { x: 5 }}}}
console.log(fn())
transpiled code:
foo = {}
function fn() {
var _foo, _foo$bar, _foo$bar$innerfoo;
if ((_foo = foo) === null || _foo === void 0 ? void 0 : (_foo$bar = _foo.bar) === null || _foo$bar === void 0 ? void 0 : (_foo$bar$innerfoo = _foo$bar.innerfoo) === null || _foo$bar$innerfoo === void 0 ? void 0 : _foo$bar$innerfoo.innerbar) return foo.bar.innerfoo.innerbar.x;
return 0;
}
console.log(fn())
foo = {bar: { innerfoo: { innerbar: { x: 5 }}}}
console.log(fn())
I am trying to log the input arguments and return value of a method called.
class A {
constructor () {
this.x = 'x'
}
callinstance(x){
// i want to log above value of x without changing class code
if (typeof this.x !== 'string') throw new Error('this.x MUST_BE_STRING')
return x + 1
// i want to log value to be returned without changing code
}
}
A.callstatic = function(x){
// i want to log above value of x without changing class code
return x + 2
// i want to log value to be returned without changing code
}
A.a = 'a' // a static property should't be touched
// i can't change above code. i also cannot put a log in the actual methods
// now here is the soution, but runs into stack overflow. For obvious reasons.
const instrument = function(prop, static) {
return function (...args) {
if (!static) static = this
console.log(args) // here instrumenting inputs
const t = static[prop](...args)
console.log(t) // here instrumenting return values
return t
}
}
// overriding all the static methods
Object.getOwnPropertyNames(A).filter(p => typeof A[p] === 'function').forEach((prop) => {
A[prop] = instrument(prop, A)
})
// overriding all the instance methods
Object.getOwnPropertyNames(A.prototype).filter(p => typeof A.prototype[p] === 'function' && p !== 'constructor').forEach((prop) => {
A.prototype[prop] = instrument(prop)
})
// validation
console.log(A.callstatic(1))
console.log((new A()).callinstance(11))
console.log(A.a)
Solution above going in stack overflow for obvious reasons.
Any workaround to achieve my goal? without changing the class code?
PS : this is not a homework, i am just curious to have a solution for debugging tool.
Your problem is getting stack overflow error because you are overriding your own method
which means
/*
A[prop] = instrument(prop, A); <-- this turns into as followed (replacing A[prop] to method name)
function callinstance = () {
console.log(args) // here instrumenting inputs
const t = callinstance(...args) // <-- recursion happens here
console.log(t) /
}
You can do some work around to solve this problem, create a copy of the function, and override your class method, inside your custom function call the oldMethod
// overriding all the static methods
Object.getOwnPropertyNames(A).filter(p => typeof A[p] === 'function').forEach((prop) => {
let oldMethod = A[prop];
A[prop] = function(args) {
console.log(args);
let x = oldMethod(args);
console.log(x);
};
})
// overriding all the instance methods
Object.getOwnPropertyNames(A.prototype).filter(p => typeof A.prototype[p] === 'function' && p !== 'constructor').forEach((prop) => {
let oldMethod = A.prototype[prop];
A.prototype[prop] = function(...args) {
console.log(...args);
let x = oldMethod.call(this, ...args);
console.log(x);
};
})
Why is returnObjectcausing an compilation TypeError returnObject2 isn't?
Normally, void || something should return something while void && something should return void.
But in Typescript, it's the opposite that occurs.
var returnVoid = function(){};
var returnObject = function(){ //Typescript compilation TypeError.
//No best common type exists among return expressions.
if(Math.random() < 0.5)
return new Error();
return returnVoid() || null; //always return null
}
var returnObject2 = function(){ //works
if(Math.random() < 0.5)
return new Error();
return returnVoid() && null; //always return undefined
}
Note: TypeError occurs during the compilation, not in the runtime.
EDIT: I did another test. Shouldn't returnNum2 be () => number too considering (undefined || something) === something? Note: Same behaviour for void 0.
var returnNum = function(){ //() => number
return undefined || 0;
}
var returnVoid = function(){};
var returnNum2 = function(){ //() => void | number
return returnVoid() || 0;
}
It's been pointed out in comments, but I think in general you just need to understand that function(){} will return undefined, which has specified behavior for logical operators.
For undefined && somevalue, undefined will always be returned. For undefined || somevalue, somevalue will be evaluated and returned.
Here's a good reference for more information: http://www.javascriptkit.com/jsref/comparison_operators.shtml
EDIT: The question isn't about what is returned for the logical operation, but why typescript gives error TS2354: No best common type exists among return expressions. on compilation.
This does seem like an error, but may make sense in the context. If you replace the logical operators with just a call to returnVoid() only, you'll get the same error in both functions. Static typing allows the && operator to short-circuit entirely for typing, since something && null will never evaluate to a type, but something || null could depending on what the something is.
Related to this, in typescript you cannot explicitly specify null or undefined as a return type for a function.
While I understand why this may be the case, I agree it is a little odd. It might be worth checking with the folks who make Typescript and filing a bug.
TypeScript doesn't special case expressions of the form undefined || T or void || T to be T because a) you shouldn't write that code (use the comma operator!) and b) it's not safe to write this code because return value contravariance means you're not guaranteed to have a falsy value just because you have a void-returning function reference.
Consider if you wrote code like this:
type callback = (arg: any) => void;
function doSomething(x: callback) {
return x(10) || 'Hello, world!';
}
var x = [];
var add = (arg: any) => x.push(arg);
console.log(doSomething(add)); // Prints '1', not 'Hello, world!'
Im using the following template in IE11 and can not figure out why the sidebar sings in every time navigation is happening. When developer tools are open it behaves as I would like it to. It is easily demoed by clicking on any one of the tabs under UI element in the tree while running IE11. However you will notice if F12 developer tools are open the side bar does not slide in every time navigation happens. This is not an issue in chrome. There is an error with fastclick that may show up however I have ran without fastclick and it still happens. Any help would be great. Thanks.
https://almsaeedstudio.com/themes/AdminLTE/pages/UI/general.html
Try removing any console.log() from your code.
console.log() which is to help out when debugging Javascript can cause IE to completely stop processing scripts on the page. To add to the mystery, if you keep watching your page in IE with devtools open - you won’t notice an issue at all.
Explanation
The reason for this is the console object is not instantiated unless devtools is open in IE. Otherwise, you will see one of two things:
Javascript won’t execute correctly
Console has cryptic errors, such as ‘object is undefined’ or others of that nature
Nine times out of ten, you have an errant console.log in the code somewhere. This does not affect any browser other than IE.
Another potential cause, especially if you are performing ajax calls, is the ajax response may be cached when dev tools are closed, but refreshed from the server when dev tools are open.
In IE, open the Network tab of Developer Tools, click the play icon, and un-set the Always refresh from server button. Then watch to see if any of your ajax calls are coming back with a response code of 304 (Not modified). If they are, then you are not getting fresh data from the server and you need to update the cacheability settings on the page that is being called via ajax.
Adding onto the already great answers (since I can't comment - requires 50 rep points), agreeing with the answer from #sam100rav and the comment from #storsoc, I discovered that in IE11 version 11.1387.15063.0 with updated version 11.0.90 (KB4462949), that window.console indeed exists as an empty object (window.console = {}). Hence, I used a variation of the polyfill from #storsoc as shown below.
if (!window.console || Object.keys(window.console).length === 0) {
window.console = {
log: function() {},
info: function() {},
error: function() {},
warn: function() {}
};
}
As pointed out already it's because IE11 + Edge<=16 is so stupid that it doesn't support console unless developer tools is opened... So if you open that to disable caching you won't see any issues and you might think that the issue was just due to browser cache... but nope.. :#
I made this "polyfill" for it (doesn't really polyfill, but makes IE not throw any errors). Add it as early as possible on your site as any js might be using console.log or console.warn etc.
window.console = typeof window.console !== 'object' || {};
console.warn = typeof console.warn === 'function' || function () {
return this;
};
console.log = typeof console.log === 'function' || function () {
return this;
};
console.info = typeof console.info === 'function' || function () {
return this;
};
console.error = typeof console.error === 'function' || function () {
return this;
};
console.assert = typeof console.assert === 'function' || function () {
return this;
};
console.dir = typeof console.dir === 'function' || function () {
return this;
};
console.table = typeof console.table === 'function' || function () {
return this;
};
console.group = typeof console.group === 'function' || function () {
return this;
};
console.groupEnd = typeof console.groupEnd === 'function' || function () {
return this;
};
console.time = typeof console.time === 'function' || function () {
return this;
};
console.timeEnd = typeof console.timeEnd === 'function' || function () {
return this;
};
console.timeLog = typeof console.timeLog === 'function' || function () {
return this;
};
console.trace = typeof console.trace === 'function' || function () {
return this;
};
console.clear = typeof console.clear === 'function' || function () {
return this;
};
console.count = typeof console.count === 'function' || function () {
return this;
};
console.debug = typeof console.debug === 'function' || function () {
return this;
};
console.dirxml = typeof console.dirxml === 'function' || function () {
return this;
};
console.groupCollapsed = typeof console.groupCollapsed === 'function' || function () {
return this;
};
I'm assuming you've fixed this since you posted it as I can not see the behavior you describe in your link.
However, I have recently run into a similar issue where the dev tools being open changed the behavior not because of console issues, but because opening the tools changed the width of the window. It was the window width difference that triggered an underlying bug in my case.
Related post here.
It's possible you've got the compatibility mode set to a later version of IE in your developer console (see the highlighted section)
I played with generators in Nodejs v0.11.2 and I'm wondering
how I can check that argument to my function is generator function.
I found this way typeof f === 'function' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function) but I'm not sure if this is good (and working in future) way.
What is your opinion about this issue?
We talked about this in the TC39 face-to-face meetings and it is deliberate that we don't expose a way to detect whether a function is a generator or not. The reason is that any function can return an iterable object so it does not matter if it is a function or a generator function.
var iterator = Symbol.iterator;
function notAGenerator() {
var count = 0;
return {
[iterator]: function() {
return this;
},
next: function() {
return {value: count++, done: false};
}
}
}
function* aGenerator() {
var count = 0;
while (true) {
yield count++;
}
}
These two behave identical (minus .throw() but that can be added too)
In the latest version of nodejs (I verified with v0.11.12) you can check if the constructor name is equal to GeneratorFunction. I don't know what version this came out in but it works.
function isGenerator(fn) {
return fn.constructor.name === 'GeneratorFunction';
}
this works in node and in firefox:
var GeneratorFunction = (function*(){yield undefined;}).constructor;
function* test() {
yield 1;
yield 2;
}
console.log(test instanceof GeneratorFunction); // true
jsfiddle
But it does not work if you bind a generator, for example:
foo = test.bind(bar);
console.log(foo instanceof GeneratorFunction); // false
I'm using this:
var sampleGenerator = function*() {};
function isGenerator(arg) {
return arg.constructor === sampleGenerator.constructor;
}
exports.isGenerator = isGenerator;
function isGeneratorIterator(arg) {
return arg.constructor === sampleGenerator.prototype.constructor;
}
exports.isGeneratorIterator = isGeneratorIterator;
In node 7 you can instanceof against the constructors to detect both generator functions and async functions:
const GeneratorFunction = function*(){}.constructor;
const AsyncFunction = async function(){}.constructor;
function norm(){}
function*gen(){}
async function as(){}
norm instanceof Function; // true
norm instanceof GeneratorFunction; // false
norm instanceof AsyncFunction; // false
gen instanceof Function; // true
gen instanceof GeneratorFunction; // true
gen instanceof AsyncFunction; // false
as instanceof Function; // true
as instanceof GeneratorFunction; // false
as instanceof AsyncFunction; // true
This works for all circumstances in my tests. A comment above says it doesn't work for named generator function expressions but I'm unable to reproduce:
const genExprName=function*name(){};
genExprName instanceof GeneratorFunction; // true
(function*name2(){}) instanceof GeneratorFunction; // true
The only problem is the .constructor property of instances can be changed. If someone was really determined to cause you problems they could break it:
// Bad people doing bad things
const genProto = function*(){}.constructor.prototype;
Object.defineProperty(genProto,'constructor',{value:Boolean});
// .. sometime later, we have no access to GeneratorFunction
const GeneratorFunction = function*(){}.constructor;
GeneratorFunction; // [Function: Boolean]
function*gen(){}
gen instanceof GeneratorFunction; // false
TJ Holowaychuk's co library has the best function for checking whether something is a generator function. Here is the source code:
function isGeneratorFunction(obj) {
var constructor = obj.constructor;
if (!constructor) return false;
if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
return isGenerator(constructor.prototype);
}
Reference: https://github.com/tj/co/blob/717b043371ba057cb7a4a2a4e47120d598116ed7/index.js#L221
As #Erik Arvidsson stated, there is no standard-way to check if a function is a generator function. But you can, for sure, just check for the interface, a generator function fulfills:
function* fibonacci(prevPrev, prev) {
while (true) {
let next = prevPrev + prev;
yield next;
prevPrev = prev;
prev = next;
}
}
// fetch get an instance
let fibonacciGenerator = fibonacci(2, 3)
// check the interface
if (typeof fibonacciGenerator[Symbol.iterator] == 'function' &&
typeof fibonacciGenerator['next'] == 'function' &&
typeof fibonacciGenerator['throw'] == 'function') {
// it's safe to assume the function is a generator function or a shim that behaves like a generator function
let nextValue = fibonacciGenerator.next().value; // 5
}
Thats's it.
The old school Object.prototype.toString.call(val) seems to work also. In Node version 11.12.0 it returns [object Generator] but latest Chrome and Firefox return [object GeneratorFunction].
So could be like this:
function isGenerator(val) {
return /\[object Generator|GeneratorFunction\]/.test(Object.prototype.toString.call(val));
}
function isGenerator(target) {
return target[Symbol.toStringTag] === 'GeneratorFunction';
}
or
function isGenerator(target) {
return Object.prototype.toString.call(target) === '[object GeneratorFunction]';
}
Mozilla javascript documentation describes Function.prototype.isGenerator method MDN API. Nodejs does not seem to implement it. However if you are willing to limit your code to defining generators with function* only (no returning iterable objects) you can augment it by adding it yourself with a forward compatibility check:
if (typeof Function.prototype.isGenerator == 'undefined') {
Function.prototype.isGenerator = function() {
return /^function\s*\*/.test(this.toString());
}
}
I checked how koa does it and they use this library: https://github.com/ljharb/is-generator-function.
You can use it like this
const isGeneratorFunction = require('is-generator-function');
if(isGeneratorFunction(f)) {
...
}
By definition, a generator is simply a function that, when called, returns an iterator. So, I think you have only 2 methods that will always work:
1. Accept any function as a generator
2. Actually call the function and check if the result is an iterator
#2 may involve some overhead and if you insist on avoiding that overhead, you're stuck with #1. Fortunately, checking if something is an iterator is pretty simple:
if (object === undefined) || (object === null) {
return false
}
return typeof object[Symbol.iterator] == 'function'
FYI, that still doesn't guarantee that the generator will work OK since it's possible to create an object with the key Symbol.iterator that has a function value that does not, in fact, return that right type of thing (i.e. an object with value and done keys). I suppose you could check if the function has a next() method, but I wouldn't want to call that multiple times to see if all the return values have the correct structure ;-)
A difficulty not addressed on here yet is that if you use the bind method on the generator function, it changes the name its prototype from 'GeneratorFunction' to 'Function'.
There's no neutral Reflect.bind method, but you can get around this by resetting the prototype of the bound operation to that of the original operation.
For example:
const boundOperation = operation.bind(someContext, ...args)
console.log(boundOperation.constructor.name) // Function
Reflect.setPrototypeOf(boundOperation, operation)
console.log(boundOperation.constructor.name) // GeneratorFunction