Pure JavaScript each function not working - javascript

In pure JavaScript, I am trying to make the jQuery.each function. So far I have just copied parts from the query source code.
Here is what I have so far:
var class2type = {
"[object Boolean]": "boolean",
"[object Number]": "number",
"[object String]": "string",
"[object Function]": "function",
"[object Array]": "array",
"[object Date]": "date",
"[object RegExp]": "regexp",
"[object Object]": "object",
"[object Error]": "error"
},
core_toString = class2type.toString;
function type(obj) {
if (obj == null) {
return String(obj);
}
return typeof obj === "object" || typeof obj === "function" ? class2type[core_toString.call(obj)] || "object" : typeof obj;
}
function isWindow(obj) {
return obj != null && obj == obj.window;
}
function isArraylike(obj) {
var length = obj.length,
type = type(obj);
if (isWindow(obj)) {
return false;
}
if (obj.nodeType === 1 && length) {
return true;
}
return type === "array" || type !== "function" && (length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj);
}
function each( obj, callback, args ) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike( obj );
if ( args ) {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
}
} else {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
}
}
return obj;
}
It should work fine, but when I I try to run the following code:
each([1, 2], function( index, value ) {
alert( index + ": " + value );
});
I get the following error: TypeError: 'undefined' is not a function (evaluating 'type(obj)') This refers to here:
23| function isArraylike(obj) {
24| var length = obj.length,
25| type = type(obj);
Why won't this code work? I just used parts directly from jQuery's source code.
Thank you.

The problem is one of variable hoisting and shadowing. You have a type function outside of the current scope and you expect that in the statement on line 25 it is the one used as a function and then the result is passed to the local variable with the same name:
function type () {};
function isArraylike(){
var type = type(1);
};
In fact, what the code looks like due to variable hoisting is:
function type() {};
function isArraylike(){
var type; // type is undefined here
type = type(1);
};
So you can see that throughout the isArraylike function, type will always be a variable and it will never reference the function from the outer scope. The fix is simple: use another name either for the function or the variable.

Related

Getting TypeError when iterating over an object

I am using Rails, and I am getting an error in my jQuery code:
Uncaught TypeError: Cannot read property 'length' of undefined
The actual code where I am getting the error:
// args is for internal usage only
each: function( obj, callback, args ) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike( obj );
if ( args ) {
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
} else {
for ( i in obj ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
}
how about try this?
var length = Object.keys(obj).length
or
var length = 0;
for (var o in obj) {
length++;
}

Is it possible to traverse object in JavaScript in non-recursive way?

For example we have a JavaScript object which can contain other objects with arbitrary depth of nesting. Is it possible to traverse every element of this object not using recursion?
If not then what are minimum requirement for data structure to make it traversal using non-recursive iteration?
As SLaks wrote above any recursion can be represented as loop with stack. So after thinking a while I came up with next solution:
var myobj = {
one: "hello",
two: "world",
three: {
one: 1,
two: 2,
three: 4,
four: {
one: true,
two: false
}
},
four: "!"
};
function traverse(obj) {
var stack = [];
stack.push(obj);
while (stack.length) {
for (var j in stack[0]) {
if (typeof stack[0][j] === 'object') {
stack.push(stack[0][j]);
} else {
console.log('%s: %s', j, stack[0][j]);
}
}
stack.shift();
}
}
traverse(myobj);
Traversing arbitrary object requires support for primitive types as well as complex types (including arrays), as well as protection against cyclic references. The following is a sample non recursive function that should traverse and stringify any object:
function FlatStringify( Arg )
{
var ToString = '', ArgObject, Resume, nStartIndex, Stack = [], Processed = []
do
{
if( Array.isArray( Arg ) )
{
var nIndex, nLen = Arg.length
if( Resume )
{
nStartIndex = Resume[1] + 1
ArgObject = Resume[2]
Resume = undefined
if( nStartIndex < nLen )
{
ToString += ', '
}
}
else
{
if( Processed.indexOf( ArgObject ? ArgObject : Arg ) >= 0 )
{
ToString += '{ <cyclic>'
nStartIndex = nLen
}
else
{
Processed.push( ArgObject ? ArgObject : Arg )
nStartIndex = 0
ToString += '{'
}
}
nIndex = nStartIndex
if( nIndex < nLen )
{
// Save our Array and loop position
Stack.push( [ Arg, nIndex, ArgObject ] )
// Restore Object Context if any!
if( ArgObject )
{
ToString += ' ' + Arg[ nIndex ] + ': '
Arg = ArgObject[ Arg[ nIndex ] ]
}
else
{
ToString += ' '
Arg = Arg[ nIndex ]
}
nIndex++
}
if( nIndex >= nLen )
{
ToString += ' }'
ArgObject = undefined
}
else
{
// Skip to the while( ... )
continue
}
}
else if( typeof Arg === 'object' )
{
if( Arg == null )
{
ToString += 'null'
}
else
{
ArgObject = Arg
Arg = Object.keys( ArgObject )
continue
}
}
else if( typeof Arg === 'string' )
{
ToString += "'" + Arg + "'"
}
else if( typeof Arg === 'function' )
{
ToString += 'function ' + Arg.name + '(){...}'
}
else if( typeof Arg === 'number' )
{
ToString += Arg
}
else if( typeof Arg === 'boolean' )
{
ToString += Arg
}
else
{
//console.log( typeof Arg )
ToString += typeof Arg//String( Arg )
}
if( Stack.length )
{
//console.log( 'Resuming: ' + Stack.length + '(' + nLoops + ')' )
Resume = Stack.pop()
Arg = Resume[0]
}
}
while( Resume || ArgObject || Stack.length )
return ToString
}

How to compare two JSON have the same properties without order?

I have tried to compare those two JSON objects:
<input type="hidden" id="remoteJSON" name="remoteJSON" value='{"allowExternalMembers": "false", "whoCanJoin": "CAN_REQUEST_TO_JOIN"}' /><br />
<input type="hidden" id="localJSON" name="localJSON" value='{"whoCanJoin": "CAN_REQUEST_TO_JOIN", "allowExternalMembers": "false"}' /><br />
I got values with javascript and I tried to compare with : JSON.stringify(remoteJSON) == JSON.stringify(localJSON) but this return false: it seems that the order of the properties is important.
And I even tried deep compare with this solution and always got a false return.
Is there a fast way to do the issue with jQuery (i.e. libraries for comparing JSON) ?
Lodash _.isEqual allows you to do that:
var
remoteJSON = {"allowExternalMembers": "false", "whoCanJoin": "CAN_REQUEST_TO_JOIN"},
localJSON = {"whoCanJoin": "CAN_REQUEST_TO_JOIN", "allowExternalMembers": "false"};
console.log( _.isEqual(remoteJSON, localJSON) );
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.10/lodash.min.js"></script>
Lodash isEqual() method is the best way to compare two JSON object.
This will not consider the order of the keys in object and check for the equality of object. Example
const object1 = {
name: 'ABC',
address: 'India'
};
const object2 = {
address: 'India',
name: 'ABC'
};
JSON.stringify(object1) === JSON.stringify(object2)
// false
_.isEqual(object1, object2)
// true
Reference - https://lodash.com/docs/#isEqual
If sequence is not going to change than JSON.stringify() will be fast as compared to Lodash's isEqual() method.
Reference - https://www.measurethat.net/Benchmarks/Show/1854/0/lodash-isequal-test
DeepCompare method to compare two json objects..
deepCompare = (arg1, arg2) => {
if (Object.prototype.toString.call(arg1) === Object.prototype.toString.call(arg2)){
if (Object.prototype.toString.call(arg1) === '[object Object]' || Object.prototype.toString.call(arg1) === '[object Array]' ){
if (Object.keys(arg1).length !== Object.keys(arg2).length ){
return false;
}
return (Object.keys(arg1).every(function(key){
return deepCompare(arg1[key],arg2[key]);
}));
}
return (arg1===arg2);
}
return false;
}
console.log(deepCompare({a:1},{a:'1'})) // false
console.log(deepCompare({a:1},{a:1})) // true
This code will verify the json independently of param object order.
var isEqualsJson = (obj1,obj2)=>{
keys1 = Object.keys(obj1);
keys2 = Object.keys(obj2);
//return true when the two json has same length and all the properties has same value key by key
return keys1.length === keys2.length && Object.keys(obj1).every(key=>obj1[key]==obj2[key]);
}
var obj1 = {a:1,b:2,c:3};
var obj2 = {a:1,b:2,c:3};
console.log("json is equals: "+ isEqualsJson(obj1,obj2));
alert("json is equals: "+ isEqualsJson(obj1,obj2));
Easy way to compare two json string in javascript
var obj1 = {"name":"Sam","class":"MCA"};
var obj2 = {"class":"MCA","name":"Sam"};
var flag=true;
if(Object.keys(obj1).length==Object.keys(obj2).length){
for(key in obj1) {
if(obj1[key] == obj2[key]) {
continue;
}
else {
flag=false;
break;
}
}
}
else {
flag=false;
}
console.log("is object equal"+flag);
I adapted and modified the code from this tutorial to write a function that does a deep comparison of two JS objects.
const isEqual = function(obj1, obj2) {
const obj1Keys = Object.keys(obj1);
const obj2Keys = Object.keys(obj2);
if(obj1Keys.length !== obj2Keys.length) {
return false;
}
for (let objKey of obj1Keys) {
if (obj1[objKey] !== obj2[objKey]) {
if(typeof obj1[objKey] == "object" && typeof obj2[objKey] == "object") {
if(!isEqual(obj1[objKey], obj2[objKey])) {
return false;
}
}
else {
return false;
}
}
}
return true;
};
The function compares the respective values of the same keys for the two objects. Further, if the two values are objects, it uses recursion to execute deep comparison on them as well.
Hope this helps.
lodash will work, tested even for angular 5, http://jsfiddle.net/L5qrfx3x/
var remoteJSON = {"allowExternalMembers": "false", "whoCanJoin":
"CAN_REQUEST_TO_JOIN"};
var localJSON = {"whoCanJoin": "CAN_REQUEST_TO_JOIN",
"allowExternalMembers": "false"};
if(_.isEqual(remoteJSON, localJSON)){
//TODO
}
it works, for installation in angular, follow this
This function works for objects with simple primitives:
function compareObjects(o1, o2) {
const normalizedObj1 = Object.fromEntries(Object.entries(o1).sort(([k1], [k2]) => k1. localeCompare(k2)));
const normalizedObj2 = Object.fromEntries(Object.entries(o2).sort(([k1], [k2]) => k1. localeCompare(k2)));
return JSON.stringify(normalizedObj1) === JSON.stringify(normalizedObj2);
}
compareObjects({a: 1, b: 2}, {b: 2, a: 1}); // true
If the objects contain nested objects, it won't work because we need to normalize them recursively
This question reminds of how to determine equality for two JavaScript objects.
So, I would choose this general function
Compares JS objects:
function objectEquals(x, y) {
// if both are function
if (x instanceof Function) {
if (y instanceof Function) {
return x.toString() === y.toString();
}
return false;
}
if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
if (x === y || x.valueOf() === y.valueOf()) { return true; }
// if one of them is date, they must had equal valueOf
if (x instanceof Date) { return false; }
if (y instanceof Date) { return false; }
// if they are not function or strictly equal, they both need to be Objects
if (!(x instanceof Object)) { return false; }
if (!(y instanceof Object)) { return false; }
var p = Object.keys(x);
return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) ?
p.every(function (i) { return objectEquals(x[i], y[i]); }) : false;
}
In VueJs function you can use this as well... A working solution using recursion. Base credits Samadhan Sakhale
check_objects(obj1, obj2) {
try {
var flag = true;
if (Object.keys(obj1).length == Object.keys(obj2).length) {
for (let key in obj1) {
if(typeof (obj1[key]) != typeof (obj2[key]))
{
return false;
}
if (obj1[key] == obj2[key]) {
continue;
}
else if(typeof (obj1[key]) == typeof (new Object()))
{
if(!this.check_objects(obj1[key], obj2[key])) {
return false;
}
}
else {
return false;
}
}
}
else {
return false
}
}
catch {
return false;
}
return flag;
},
We use the node-deep-equal project which implements the same deep-equal comparison as nodejs
A google serach for deep-equal on npm will show you many alternatives
If you are trying to compare those two objects with the same goal as mine: which is to deserialize just an valid object, in that case, I would recommend you to use the following method:
using Newtonsoft.Json;
try
{
var myObj = JsonConvert.DeserializeObject<**YourTargetObjectType**>(jsonString, new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Error
});
}catch (MissingMemberException mme)
{
throw;
}
If an error get caught, so your object does not belong to YourTargetObjectType. Otherwise it is good to go, and you can do the same for the second object.
The JsonSerializerSetings with the MissingMemberHandling do the trick. And in the mme exception object you can check which property property failed. This validates extra properties, missing ones and/or misspelled properties.
So in your case, you should have a Object which will be used as reference to compare both objects.
There is Intellij Idea plugin for this purpose: https://plugins.jetbrains.com/plugin/20169-json-comparator?preview=true
It sorts all fields and objects in arrays recursively
with JSONata simply use = operator
async function compare() {
const jsonEQObj = {
object1: {
name: 'ABC',
address: 'India'
},
object2: {
address: 'India',
name: 'ABC'
}
};
let isEqual = await jsonata('object1=object2').evaluate(jsonEQObj)
console.log(isEqual)
jsonEQObj.object1.name = 'X';
isEqual = await jsonata('object1=object2').evaluate(jsonEQObj)
console.log(isEqual)
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONata test</title>
<script src="https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js"></script>
</head>
<body onload=compare()>
</body>
</html>
Due to #zerkems comment:
i should convert my strings to JSON object and then call the equal method:
var x = eval("(" + remoteJSON + ')');
var y = eval("(" + localJSON + ')');
function jsonequals(x, y) {
// If both x and y are null or undefined and exactly the same
if ( x === y ) {
return true;
}
// If they are not strictly equal, they both need to be Objects
if ( ! ( x instanceof Object ) || ! ( y instanceof Object ) ) {
return false;
}
// They must have the exact same prototype chain, the closest we can do is
// test the constructor.
if ( x.constructor !== y.constructor ) {
return false;
}
for ( var p in x ) {
// Inherited properties were tested using x.constructor === y.constructor
if ( x.hasOwnProperty( p ) ) {
// Allows comparing x[ p ] and y[ p ] when set to undefined
if ( ! y.hasOwnProperty( p ) ) {
return false;
}
// If they have the same strict value or identity then they are equal
if ( x[ p ] === y[ p ] ) {
continue;
}
// Numbers, Strings, Functions, Booleans must be strictly equal
if ( typeof( x[ p ] ) !== "object" ) {
return false;
}
// Objects and Arrays must be tested recursively
if ( !equals( x[ p ], y[ p ] ) ) {
return false;
}
}
}
for ( p in y ) {
// allows x[ p ] to be set to undefined
if ( y.hasOwnProperty( p ) && ! x.hasOwnProperty( p ) ) {
return false;
}
}
return true;
}

Checking the types of function arguments in Javascript

In Javascript, is there any way to check the types of a function's arguments? I want to write a function called checkTypes that does the following:
function checkTypes(typeArr){
//if the types do not match typeArr, throw an error
}
function exampleUsage(arr1, arr2, num1){
checkTypes("object", "object", "number");
//throw an error if the types do not match the corresponding elements
}
You can use the the typeOf function adapted from this post Fixing the JavaScript typeof operator combined with this function:
function typeOf( obj ) {
return ({}).toString.call( obj ).match(/\s(\w+)/)[1].toLowerCase();
}
function checkTypes( args, types ) {
args = [].slice.call( args );
for ( var i = 0; i < types.length; ++i ) {
if ( typeOf( args[i] ) != types[i] ) {
throw new TypeError( 'param '+ i +' must be of type '+ types[i] );
}
}
}
function foo( a,b,c ) {
checkTypes( arguments, ['string', 'number', 'array'] );
return 'foo';
}
console.log( foo( 'a', 1, [2] ) ); //=> foo
console.log( foo( 1, 1, [2] ) );
//^ Uncaught TypeError: param 0 must be of type string
Do not use typeof in this case. It's problematic for several reasons:
typeof null // 'object'
typeof [] // 'object'
typeof 'foo' // 'string'
typeof new String('foo') // 'object'
'foo' == new String('foo') // true
Instead, use Object::toString:
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call([]) // '[object Array]'
Object.prototype.toString.call('foo') // '[object String]'
Object.prototype.toString.call(new String('foo')) // '[object String]'
A decorator would meet your requirements:
var getType = function(value) {
return Object.prototype.toString.call(value)
.replace(/^\[object |\]$/g, '').toLowerCase();
};
var checkTypes = function(types, fn) {
return function() {
var args = Array.prototype.slice.call(arguments, 0);
for (var idx = 0; idx < types.length; idx += 1) {
var expected = types[idx];
var received = getType(args[idx]);
if (received != expected) {
throw new TypeError('expected ' + expected + '; received ' + received);
}
}
fn.apply(null, args);
};
};
var exampleUsage = checkTypes(['array', 'array', 'number'], function(arr1, arr2, num1) {
console.log('arr1:', arr1);
console.log('arr2:', arr2);
console.log('num1:', num1);
});
Usage examples:
exampleUsage([], [], 0);
// arr1: []
// arr2: []
// num1: 0
exampleUsage([], [], 'foo');
// TypeError: expected number; received string
You can use a modified version of typeof and the arguments pseudo-array to get each argument type and compare it to your desired set of types:
// from Doug Crockford http://javascript.crockford.com/remedial.html
function typeOf(value) {
var s = typeof value;
if (s === 'object') {
if (value) {
if (Object.prototype.toString.call(value) == '[object Array]') {
s = 'array';
}
} else {
s = 'null';
}
}
return s;
}
function checkTypes(argList, typeList) {
for (var i = 0; i < typeList.length; i++) {
if (typeOf(argList[i]) !== typeList[i]) {
throw 'wrong type: expecting ' + typeList[i] + ", found " + typeOf(argList[i]);
}
}
}
Working demo: http://jsfiddle.net/jfriend00/ywyLe/
Example Usage:
function exampleUsage(arr1, arr2, num1){
//throw an error if the types do not match the corresponding elements
checkTypes(arguments, ["array", "array", "number"]);
}
You are looking for typeof operator.
The typeof function return object for most of the thing,
alert(typeof("this is string")); /* string */
alert(typeof(1234)); /* number */
alert(typeof([])); /* object */
alert(typeof({})); /* object */
alert(typeof(new Date)); /* object */
alert(typeof(function(){})); /* function */
but jQuery can identify by this function jQuery.type( obj )
http://api.jquery.com/jQuery.type/
If anyone is looking for require-like environment solution, I can recommend typeof-arguments package.
const checkTypes = require('typeof-arguments');
function exampleUsage(arr1, arr2, num1){
checkTypes(arguments,["object", "object", "number"]);
//throw an error if the types do not match the corresponding elements
}
JavaScript is bad for types.
Also, you can not magically access the parent's function arguments from a calling function.
If you don't want to have a big headache, use some simple library to check types.
For example, using underscore.js you could write something like this:
function exampleUsage(arr1, arr2, num1) {
if(!_.isArray(arr1) || !_.isArray(arr2) || !_.isNumber(num1) {
throw "Wrong types"
}
// do other stuff
}
You are probably afraid of types because you are probably new to dynamic languages. You will see that is not as bad as it looks like, but JavaScrip IS BAD (for a lot other reasons)

How to determine if a javascript object is simple or complex?

Basically I need to tell apart the following two:
var simple = 5 // or "word", or 56.78, or any other "simple" object
var complex = {propname: "propvalue", "otherprop": "othervalue"}
Using typeof operator you can determine the following:
"number" Operand is a number
"string" Operand is a string
"boolean" Operand is a Boolean
"object" Operand is an object
"undefined" Operand is not defined.
Edited:
As it was suggested in a comment you may want to also check if value is null, as typeof null will return object.
You could use typeof:
typeof 5 == "number";
typeof 1.5 == "number";
typeof true == "boolean";
typeof "word" == "string";
typeof {} == "object";
Basically:
if(obj == null) {
//null or undefined
}
else if(typeof obj == "object") {
//It's "complex"
}
else {
//Primitive or "simple"
}
Note: null will return "object", so you need to check for it.
The problem is that more than just {} returns a type of 'object'
typeof 5 == 'number'
typeof NaN == 'number'
typeof 'test' == 'string'
typeof true == 'boolean'
typeof undefined == 'undefined'
typeof null == 'object'
typeof /asdf/ == 'object' // this is true in some engines, like Firefox's. Not in V8 (in which it is 'function')
typeof [] == 'object'
typeof {} == 'object'
But, by using toString you can check further:
toString.call(null) == '[object Window]' // or '[object global]' or '[object Null]' - depends on engine
toString.call(/asdf/) == '[object RegExp]'
toString.call([]) == '[object Array]'
toString.call({}) == '[object Object]'
So, the best way to check is:
var test;
test = {};
typeof test == 'object' && toString.call(test) == '[object Object]'; // true
test = [];
typeof test == 'object' && toString.call(test) == '[object Object]'; // false
// et cetera
Hope that helps
Credit here
Object.prototype.getName = function() {
var funcNameRegex = /function (.{1,})\(/;
var results = (funcNameRegex).exec((this).constructor.toString());
return (results && results.length > 1) ? results[1] : "";
};
var simple = 5; // or "word", or 56.78, or any other "simple" object
var complex = { propname : "propvalue"
, "otherprop" : "othervalue"
};
simple.getName(); // returns: "Number"
complex.getName(); // returns: "Object"
Try the following
if (typeof obj === 'object') {
// It's complex
} else {
// It's not
}
In your case:
var simple = 5; // number, not an object
var simple = new Number(5); // now it is an object, but still the value is 5
var complex = {propname: "propvalue", "otherprop": "othervalue"};
for ( property in complex ) {
if ( complex.hasOwnProperty( property ) )
{
alert ( 'composite object' );
return;
} else {
alert ( 'simple object' );
return;
}
}
As of what I understand from you question - you need to tell if the object has properties/methods.
You could just make a simple function that returns true for simple types:
function isSimple( a ) {
return (typeof a).match(/(number)|(boolean)|(string)/)
}
Note that this will return true for NaN as it's considered a number, and false for 'undefined' - but you could easily modify this to suit your specific case.
Run the snippet below to see it in action
<script>
// return true/false if typeof matches simple regex pattern
function isSimple( a ) {
return (typeof a).match(/(number)|(boolean)|(string)/);
}
// setup some tests cases
var tests = [
[1,2,3],
'hello',
7,
{ foo: 'bar' },
NaN
]
// log output of isSimple function against each test case
for( var i in tests ) {
var value = tests[ i ];
if( isSimple( value ) ) {
console.log( 'simple value', value );
} else {
console.log( 'not simple', value );
}
}
</script>

Categories