These methods test if a host object has a specific method / property and returns true or false accordingly.
First parameter has to be a reference to an object, second parameter is the name of the method / property as string.
Boolean rv = K345.isHostMethod(Object obj, String method_name);
Boolean rv = K345.isHostProperty(Object obj, String property_name);
// test if object document has a method querySelectorAll rv = K345.isHostMethod(document, 'querySelectorAll'); // test if object Math has a property PI rv = K345.isHostProperty(Math, 'PI');
K345.isHostMethod = function (obj, item) { var ty = typeof obj[item]; return 'function|unknown'.indexOf(ty) > -1 || (ty === 'object' && Boolean(obj[item])); }; K345.isHostProperty = function (obj, item) { return 'function|object'.indexOf(typeof obj[item]) > -1 && Boolean(obj[item]); };
K345.addMethod() extends an object with a new method. It is – more or less – an enhanced replacement for code like the following:
if (typeof SomeObj.someMethod !== 'function') {
SomeObj.someMethod = function() {/* … */}
}
If the browser supports it, K345.addMethod() will use Object.defineProperty() to extend the object and make the new method non-enumerable[2]
K345.addMethod(Object obj, String mName, Function meth [, Boolean force]);
K345.addMethod(Object data);
K345.addMethod() supports either 3(4) parameters or exactly one parameter, which has to be an object with properties "obj", "name", "meth" and (optional) "force" in arbitrary order.
If parameter force is set to true, an already existing method mName will be overwritten by meth. This might be helpful when testing fallback methods for older browsers or known implemetation errors..
/* define method Array.isArray() if not already defined */ K345.addMethod( Array, 'isArray', (function () { var o2s = Object.prototype.toString; return function (item) { return o2s.call(item).replace(/[^a-z]/gi, '').toLowerCase() === 'objectarray'; }; })() ); /* define Foo.prototype.bar and OVERWRITE an existing method */ K345.addMethod( Foo.prototype, 'bar', function (baz) { /* code goes here */ }, true ); /* same as before, using data object as only parameter. * order of properties is irrelevant */ K345.addMethod({ name: 'bar', obj: Foo.prototype, force: true, meth: function (baz) { /* code goes here */ } });
/** * Extend an Object with a method. * This method takes either *exactly one* parameter (an object with the following * properties: obj, name, meth ( optional: force) or three/four parameters * If "obj" is a parameter object, NO FURTHER parameters are allowed. * * @param {Object} obj * object to extend OR parameter object * @param {String} [name] * name of the method (if obj is no parameter object) * @param {Function} [meth] * method reference (if obj is no parameter object) * @param {Boolean} [force] * optional: if true, overwrite existing method (if obj is no parameter object) * * @requires K345.defProp() * @requires K345.isHostMethod() * @requires K345.isObject() */ K345.addMethod = function (obj, name, meth, force) { var argl = arguments.length, fname = 'K345.addMethod', dat; if (argl === 3 || argl === 4) { /* function parameters */ dat = {obj: obj, name: name, meth: meth, force: force}; } else if (argl === 1) { /* data object */ dat = obj; } else { throw new Error(fname + ': wrong number of arguments'); } if ( !K345.isObject(dat) || typeof dat.meth !== 'function' || typeof dat.name !== 'string' || 'object|function|unknown'.indexOf(typeof dat.obj) < 0 ) { throw new TypeError(fname + ': wrong type of argument for method or name'); } if (!K345.isObject(dat.obj) && K345.strObjFunc.indexOf(typeof dat.obj) < 0) { throw new TypeError(fname + ': can\'t add to a non object. ' + dat.name); } if (!K345.isHostMethod(dat.obj, dat.name) || dat.force === true) { /* set non-enumerable property if possible, otherwise assign value */ K345.defProp(dat.obj, dat.name, { enumerable: false, configurable: true, writable: true, value: dat.meth }); } };
[2] non-enumerable properties of an object do not appear when looping with e.g. for…in
This method returns the basic type of an item, it could be seen as a slightly more specific alternative to the typeof operator. The returned value is a lowercase string with the (partly browser specific) type of an given item. This string is an excerpt of the Object.prototype.toString() representation of the item.
This method is just for quick testing of types and not meant to be overly specific, especially in case of objects. If you need more specific results, use K345.is() instead
val = K345.getType("hi"); // returns "string". // toString() => [object String] val = K345.getType([3,4,5]); // returns "array". // toString() => [object Array] (typeof would return "object" here) val = K345.getType(new Date()); // returns "date". // toString() => [object Date] (typeof would return "object" here) val = K345.getType(/^[a-z]+/i); // returns "regexp". // toString() => [object RegExp] (typeof would return "object" here) val = K345.getType(document.createElement('div')); // returns "htmldivelement" in most browsers. // toString() => [object HTMLDivElement] // some browsers may return "htmlelement" or just "object" or some other value instead // but it still makes it easier to test for certain objects. (Example: if the return value // conains "html" it's most likely an HTML element and additional tests are only necessary, // if the substring "html" is not found
/* * get type of an item. * This function returns the lowercase part of the string representation after the * whitespace * * String representation of an item is something like [object Object], [object String], * [object HTMLDivElement] etc. * * return values would be 'object' 'string' and 'htmldivelement' for examples above */ K345.getType = (function () { var op2s = Object.prototype.toString; if (op2s.call('hi') !== '[object String]') { throw new Error('Object.prototype.toString has been modified'); } return function (item) { return op2s.call(item) .match(/\[object\s+([^\]]+)/i)[1] .toLowerCase(); }; })();
K345.defProp is a wrapper for very basic usage of Object.defineProperty(). It assigns the the value property's value of the descriptor object if Object.defineProperty() fails.
K345.defProp handles the IE8 issue [1]
/** | defines property with Object.definePropery() or sets property value as fallback. | this is just a wrapper, not a full replacement for Object.definePropery() ! | | In IE8, Object.defineProperty() can be used with DOM objects only. | | properties of opts object can be shortened to first letter | | @param {Object} obj | Object to add the property to | @param {String} prop | name of the property to be defined | @param {Object} desc | Descriptor object with writable, configurable, enumerable and value properties | @param {Boolean} [nofallback] | If »true«, no value will be assigned if defineProperty fails. | @returns {Boolean} | »true«, if property was assigned via Object.defineProperty(), »false« otherwise */ K345.defProp = function (obj, prop, desc, nofallback) { try { Object.defineProperty(obj, prop, desc); return true; } catch (ex) { if (!nofallback) { obj[prop] = desc.value; } } return false; };
[1] IE8 has an implementation of Object.defineProperty(), but it can be used on DOM objects only and throws an error otherwise.
Converts a NodeList, HTMLCollection or similar object to a "real" array.
converts object to static array; don't use this method, if live status of the list is required.
/** * create array out of HTMLCollection / Nodelist / arguments or similar * There is no need to use this function on array-like objects just to loop/alter them with * Array methods like forEach, filter, map etc. * Use Array.prototype.<arraymethod>.call(obj, fn) instead! * * @param {List_Object} alist * object to be processed. needs "length" property * @param {Number} [start=0] * optional: start index of alist. defaults to 0 * @returns {Array | null} * Array with properties of alist or null if alist is not valid */ K345.toArray = function (alist, start, end) { var a = null, len, i; if (alist !== null && typeof alist !== 'undefined' && 'length' in alist) { len = alist.length; start = start || 0; end = end || len; try { a = Array.prototype.slice.call(alist, start, end); } catch (ex) { /* fallback for crappy browsers */ a = []; for (i = start; i < end; i++) { a[i] = alist[i]; } } } return a; };
This code for Function.prototype.bind() is a polyfill[4] for old browsers. Newer Browsers already have native support for bind()
// based on mozilla polyfill // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind K345.addMethod( Function.prototype, 'bind', function (oThis) { var sli, aArgs, fToBind, fBound, Fn; if (typeof this !== 'function') { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new TypeError('Function.prototype.bind - what is trying to be bound is' + ' not callable'); } sli = Array.prototype.slice; aArgs = sli.call(arguments, 1); fToBind = this; Fn = function () {}; fBound = function () { return fToBind.apply( this instanceof Fn ? this : oThis || g$, aArgs.concat(sli.call(arguments)) ); }; Fn.prototype = this.prototype; fBound.prototype = new Fn(); return fBound; } );
[4] This polyfill is heavily based on the polyfill provided by mozilla
Object.forEach() loops over an object and executes a callback function for each own property; very similar to Array.prototype.forEach()
The processing order of object properties is not defined and may differ, although most browsers process properties in given order.
Object.forEach(Object obj, Callback func [, Mixed context]);
The callback function takes 4 parameters:
(String propertyName, Mixed propertyValue, Number loopCounter, Object obj)
The value of this in callback func defaults to obj, if parameter context is omittet.
var o = { foo: 'hi', bar: 35, baz: [2, 4, 6], yoo: /^\s*[a-z][a-z0-9]+/gi }; Object.forEach(o, function(prop, val, cnt) { console.log(cnt + ') type of ' + prop + ' is ' + K345.getType(val)); }); /* should print to console window 0) type of foo is string 1) type of bar is number 2) type of baz is array 3) type of yoo is regexp */
/** * executes a callback function for each property of an object * * @name forEach * @param {Object} obj object to loop * @param {Function} fn callback function to execute * @param {Mixed} [ctx=obj] context of 'this' in callback function */ K345.addMethod( Object, 'forEach', function (obj, fn, ctx) { var cnt = 0, /* counter */ prop; /* property name */ if (!K345.isObject(obj) || typeof fn !== 'function') { throw new TypeError('Object.forEach: wrong parameter type'); } ctx = ctx || obj; for (prop in obj) { if (K345.hasOP(obj, prop)) { fn.call(ctx, prop, obj[prop], cnt++, obj); } } } );
Tests if an object is a DOM Node element and returns true or false
/* * tests for an element's nodeType. * Must be called or preset with bind() with an Array containing number(s) as context * * call: * K345.isNodeType.call([1,3], el) // returns true if el is an Element with nodeType 1 or 3 * * preset: * var isFragment = K345.isNodeType.bind([11]); * ok = isFragment(el) // returns true if el has an nodeType of 11 * */ K345.isNodeType = function (el) { return el !== null && typeof el === 'object' && 'nodeType' in el && 'lastChild' in el && typeof el.nodeType === 'number' && Array.isArray(this) && this.indexOf(el.nodeType) > -1; }; /** * K345.isNodeElement() * Prüft, ob der Typ des übergebenen Parameters ein DOM-Node-Objekt ist. * (nur element; bei einem documentFragment oder textNode wird "false" zurückgegeben. * * @param {Mixed} el Objekt, das getestet werden soll * @returns {Boolean} "true", wenn DOM-Node-Objekt */ K345.isNodeElement = K345.isNodeType.bind([1]); /** * K345.isTextNode() * Prüft, ob der Typ des übergebenen Parameters ein TextNode-Objekt ist. * * @param {Mixed} el Objekt, das getestet werden soll * @returns {Boolean} "true", wenn Text-Node-Objekt */ K345.isTextNode = K345.isNodeType.bind([3]); /** * K345.isAppendable() * Prüft, ob der Typ des übergebenen Parameters ein DOM-Node-Objekt ist, das an an andere * DOM-Node-Objekte angehängt werden kann. * * @param {Mixed} el Objekt, das getestet werden soll * @returns {Boolean} "true", wenn element, documentFragment, commentNode oder textNode */ K345.isAppendable = K345.isNodeType.bind([1, 3, 8, 11]);
This method tests if an object has a certain property.
There are two test modes: "lax" (returns true, if object has the property (incl. inherited) and "strict", where true is only returned for native (not inherited) properties. Default is "lax".
Boolean rv = K345.hasP(Object object, String propertyName [, Boolean strict])
** CodeExtract PARSE ERROR: No Match with identifier hasp **
** CodeExtract PARSE ERROR: No Match with identifier hasp **
This method tests if the supplied object is an custom[3] object and returns true or false accordingly. This test is generic and may provide incorrect false positive results on some browsers (like older IE). More reliable results can be achieved with K345.is()
/* * tests if item is a custom object. This is only a generic test and may provide incorrect * results on some browsers. Use K345.is() for more reliable testing */ K345.isObject = function (item) { return item !== null && K345.getType(item) === 'object'; };
[3] A custom object is an object created with {} / new Object() / Object.create() / an instance of a constructor function etc.