25

Warning: creating extensions to native object and/or properties is considered bad form, and is bound to cause problems. Do not use this if it is for code that you are not using solely for you, or if you don't know how to use it properly


I know you can use Object, String, Number, Boolean, etc. to define a method, something like this:

String.prototype.myFunction = function(){return this;} //only works on strings.

But what I need to be able to do is use that on any value, and access the value in the function.

I googled, and looked here, but couldn't find anything suitable.

2/18/15 Edit: Is there any workaround to having this be a property of any Object if I use Object.prototype?
Per Request, here is the current function that is used for isString()

function isString(ins) {
    return typeof ins === "string";
}

Following on a few answers, I have come up with some code and errors caused by it.

Object.prototype.isString = function() {
    return typeof this === "string";
}
"5".isString() //returns false
"".isString()  //returns false
var str = "string"
str.isString() //returns false
Community
  • 1
  • 1
Travis
  • 1,274
  • 1
  • 16
  • 33
  • 1
    What do you mean by "Can't access the object"? – Dai Feb 18 '15 at 01:27
  • Also, `Object`, `String`, `Number` and `Boolean` are distinct types, so you will need to define the prototype member on all of them separately. – Dai Feb 18 '15 at 01:28
  • u can access the value u working on by using "this" in the function – Cracker0dks Feb 18 '15 at 01:30
  • @Cracker0dks I didn't know that, thanks for the info, but i'll leave it available in the answer instead of updating, – Travis Feb 18 '15 at 01:31
  • 3
    It's typically considered bad form to extend global object prototypes. – Ja͢ck Feb 18 '15 at 01:43
  • @Dai: No, as all inherit from `Object` one is enough – Bergi Feb 18 '15 at 01:51
  • @Bergi the ECMAScript specification (5.1, section 4.3.21) states that (`Number`, for example) a value is only a `Number object` if it was constructed using the `Number` constructor in a `new` expression and is distinct from a `Number value` which would not inherit any `Object.prototype` properties. – Dai Feb 18 '15 at 02:05
  • 1
    Yes, but attempting to access any property on a primitive results in its conversion (via [`ToObject`](http://es5.github.io/#x9.9)) to an object. – Sean Vieira Feb 18 '15 at 02:26
  • @Ja͢ck I'm well aware, just trying to find work arounds and make it possible safe to some degree for usage. – Travis Feb 18 '15 at 04:03
  • 1
    @Dai: By that argumentation, a primitive number value doesn't inherit from `Number.prototype` either :-) – Bergi Feb 18 '15 at 13:39
  • Why do you need to make this as a method, instead of `myFunc(value) { /*do something*/ };`, where you can insert anything as `value`, and call it through the same var inside the function? – AJFarkas Feb 24 '15 at 19:44
  • @AJFarkas It doesn't matter why, just how. – Travis Feb 24 '15 at 20:00
  • @Wyatt Sure it matters why: it's entirely possible that simply declaring a function is exactly what you're trying to do. On the other hand, if the question is designed to gain insight into how prototypes work, you can get better answers by asking explicitly. – AJFarkas Feb 24 '15 at 20:24
  • @AJFarkas i'm aware about how to declare a function normally, and I do explicitly ask about prototypes, did you even *read* the question? – Travis Feb 24 '15 at 20:28
  • In fact, I _did_ read the question. I wasn't suggesting you don't know how to declare a function normally, but that sometimes we overlook the simple answers. Given how ill-advised it is to extend the native Object, I think your ultimate goal is relevant to anyone trying to give a satisfactory answer. The fact that you have a bounty on a question with multiple high-quality answers suggests that people don't understand what you're asking for, or you don't understand their answers. Or did you read their answers? – AJFarkas Feb 24 '15 at 20:37
  • @AJFarkas I awarded the bounty earlier today, I was just waiting for someone to address *all* the problems. – Travis Feb 24 '15 at 23:02

10 Answers10

14

A “dot operator function” is called a method. The cleanest way to create a method in JavaScript that can work on any data type is to create a wrapper. For example:

var Wrapper = defclass({
    constructor: function (value) {
        this.value = value;
    },
    isString: function () {
        return typeof this.value === "string";
    },
    describe: function () {
        if (this.isString()) {
            alert('"' + this.value + '" is a string.');
        } else {
            alert(this.value + " is not a string.");
        }
    }
});

var n = new Wrapper(Math.PI);
var s = new Wrapper("Hello World!");

n.describe(); // 3.141592653589793 is not a string.
s.describe(); // "Hello World!" is a string.

function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

By creating your own wrapper constructor you ensure that:

  1. Your code doesn't mess with other peoples' code.
  2. Other people's code doesn't mess with your code.
  3. You keep the global scope and native prototypes clean.

Several popular JavaScript libraries like underscore and lodash create wrapper constructors for this very purpose.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • How does this work? I don't think I really understand it, could you explain it to me? – Travis Feb 18 '15 at 13:10
  • @Wyatt Sure. A wrapper is a data type that is used solely to wrap another data type so that you can provide different methods for it. For example, you might want to create a `Vector` data type which is just like an `Array` but with a predefined `length` that doesn't change. The reason is that you might want to be able to `add` or find the `dot` product of vectors (things that should not be done on arbitrary arrays, only vectors). Using a wrapper datatype promotes [composition over inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance) which is a good thing. Hope that helps. – Aadit M Shah Feb 18 '15 at 13:43
  • If you want a crash course on inheritance and how the `defclass` function works then you should read the following answer: http://stackoverflow.com/a/17893663/783743 – Aadit M Shah Feb 18 '15 at 13:46
  • Is there a way to make these callable without `new Wrapper`? I'm trying to make something similar to native methods. – Travis Feb 19 '15 at 23:41
  • 1
    Since every object inherits from `Object.prototype` you could define `isString` on `Object.prototype` as described in @Eclecticist's answer. However, defining methods on native prototypes is frowned upon because it could inadvertently cause problems. Read, [Why is extending native objects a bad practice?](http://stackoverflow.com/q/14034180/783743). – Aadit M Shah Feb 20 '15 at 02:30
9

First of all, why defining properties on Object (or other builtin types) is frowned upon - they show up in unexpected places. Here is some code that outputs the total number of feet some characters have:

var feetMap = {
  jerry : 4,
  donald : 2,
  humpty: 0  
}

function countFeet(feetMap) {
  var allFeet = 0;
  for (var character in feetMap) {
    allFeet += feetMap[character];
  }
  return allFeet;
}


console.log('BEFORE DEFINITION', countFeet(feetMap));
Object.prototype.isString = function() {
    return typeof this === "string";
};
console.log('AFTER DEFINITION', countFeet(feetMap));

Note how simply defining your isString function will influence the result of the countFeet function which now iterates over one unexpected property. Of course, this can be avoided if the iteration was protected with hasOwnProperty check, or if the property was defined as non-enumerable.

Another reason to avoid defining properties on builtin types it the possibility of collision. If everyone defined their own isNumber method that gave slightly different results depending on use cases - one could consider the string "42" a number and another could say it's not - subtile bugs would crop all over the place when people used multiple libraries.

The question is - why do you need a method that can affect any value type? A method should be something that is inherent to the class of objects it belongs to. Having an isString method makes no sense on a Number object - it simply doesn't have any relevance to Numbers.

What makes more sense is to have a function/method that can return the type of the value given to it as parameter:

var Util = Util || {};
Util.isString(value) {
  return typeof value === "string";
}

Util.isString('test') // true
Util.isString(5) // false

The reason why your current code

Object.prototype.isString = function() {
    return typeof this === "string";
}
"5".isString() //returns false
"".isString()  //returns false
var str = "string"
str.isString() //returns false

isn't working is because when you access a property on a primitive value, JS creates a wrapper object of the apropriate types and resolves the property on that wrapper object (after which it throws it away). Here is an example that should elucidate it:

Object.prototype.getWrapper = function(){
  return this;
}

console.log((5).getWrapper()); // Number [[PrimitiveValue]]:5
console.log("42".getWrapper()); // String [[PrimitiveValue]]:"42"

Note that the primitive value 5 and the object new Number(5) are different concepts.

You could alter your function to mostly work by returning the type of the primitive value. Also, don't forget to make it non-enumerable so it doesn't show up when you iterate over random Objects.

Object.defineProperty(Object.prototype, 'isString', {
   value : function() {
             return typeof this.valueOf() === "string";
           },
   enumerable : false
});
"5".isString() //returns true
"".isString()  //returns true
var str = "string"
str.isString() //returns true
Tibos
  • 27,507
  • 4
  • 50
  • 64
  • This doesn't answer the question, just a single bug, in a way that is uncalled for. – Travis Feb 22 '15 at 21:11
  • @Wyatt if you'd cross the street when the light is red and got hurt, you then ask why would you rather the answer be because you got hit by a car or because you should cross when the light is green. Breaking encapsulation is a bad thing for exactly the reasons pointed out in the answer. – HMR Feb 23 '15 at 00:42
  • @HMR if you read the question, you know I am aware of that, and trying to find ways to make it possible, and safer. – Travis Feb 23 '15 at 02:34
  • @Wyatt you read the answer and find a way to do it in the last code block. Even though you should not do it as pointed out by yourself and many others. So the safest would be to use the Util way or ignore a fundamental basic of OOP (breaking encapsulation) and change Object.prototype. – HMR Feb 23 '15 at 04:23
  • @HMR I stopped reading after all the junk I already said, thanks for pointing the *answer* out. – Travis Feb 23 '15 at 04:50
  • 1
    @Wyatt I explained why i think what you're doing is unsafe and what i think is a better solution to your problem. In the second part of the answer i explained why your current solution is buggy and i fixed both problems with it (by accessing the primitive value and defining the property as non-enumerable). Is there anything else you asked that i missed? – Tibos Feb 23 '15 at 07:13
  • No, that was all I needed, still gonna see if there's something easier that anyone gives before awarding a bounty though. – Travis Feb 23 '15 at 13:54
5
Object.prototype.isString = function() {
    return typeof this === "string";
}
"5".isString() //returns false
"".isString()  //returns false
var str = "string"
str.isString() //returns false

If anyone could explain a workaround for the function being a property of any object, and why the current method isn't working, I will provide 125 rep.

Answer:

Well in javascript, when you are calling a sub method/property of a object,
like "myFunction" (object.myFunction or object["MyFunction"])
it will start to see if the object itself have it.
IF NOT: it will follow the prototype chain(like superclass in normal oop),
until it finds a "parent/superclass" with the method/property.
The last step in this prototype chain is Object.
If Object dosnt have the method, it will return "undefined".

When you extending the Object class itself it will alway look at any object calling the method as a Object (In oop: All classes are also Object in addition the is own classtype) This is like missing a "cast" in normal OOP.

So the reason why your function returns false is that its a "object" not a "string" in this context
Try making this function:

Object.prototype.typeString = function() {
    return typeof this;
}
"5".typeString() //returns "object"

As everbody says it really bad idea to extend any of the native JS classes, but a workaround would start with something like this:

Object.prototype.objectTypeString = function(){
   return Object.prototype.toString.call(this);
}

Here is a fiddle: http://jsfiddle.net/fwLpty10/13/

Note that null dosnt have prototype and NaN (NotANumber) is condidered a Number!!! This means that you will always need to check is a variable is null, before calling this method!

Object.prototype.isString = function(){
   return Object.prototype.toString.call(this) === "[object String]";
};

Final fiddle: http://jsfiddle.net/xwshjk4x/5/

The trick here is that this methods returns the result of the toString method, that are called with "this", which means that in the context of the toString method, the object you call it on, are its own class (not just any supertype in the prototype chain )

Alf Nielsen
  • 1,361
  • 1
  • 10
  • 19
5

The code posted that extends the Object prototype will work, if corrected.

However, it makes an incorrect assumption about what this is inside the invoked method. With the posted code the following output is correct and to be expected (barring a few old implementation bugs);

"5".isString() //returns false

This is because JavaScript will "wrap" or "promote" the primitive value to the corresponding object type before it invokes the method - this is really a String object, not a string value. (JavaScript effectively fakes calling methods upon primitive values.)

Replace the function with:

Object.prototype.isString = function() {
    return this instanceof String;
}

Then:

"5".isString() // => true (a string was "promoted" to a String)
(5).isString() // => false (`this` is a Number)

Another solution to this is also to use polymorphism; with the same "pitfalls"1 of modifying standard prototypes.

Object.prototype.isString = function () { return false; }
String.prototype.isString = function () { return true; }

1The concern of adding a new enumerable property to the global protoypes can be mitigated with using defineProperty which creates a "not enumerable" property by default.

Simply change

x.prototype.y = ..

to

Object.defineProperty(x.prototype, 'y', { value: .. })

(I am not defending the use of modifying the prototype; just explaining the original problematic output and pointing out a way to prevent the enumeration behavior.)

user2864740
  • 60,010
  • 15
  • 145
  • 220
  • How about a solution to `for(i instance of someObject){}` – Travis Feb 23 '15 at 19:06
  • Someone offers a snipped in an answer somewhere above this one, and using the `instanceof` in a `for` loop, finds that the method is a property of any objects. – Travis Feb 23 '15 at 19:10
  • Nvm found it: A good reason NOT to add properties this way to Object.prototype is that this will pick up the isString property in all iterations of any object as in x = {}; for (prop in x) {}, jsfiddle.net/jfriend00/5qLwa9L4. This can easily break existing code. – jfriend00 Feb 18 at 3:02 – Travis Feb 23 '15 at 19:14
  • Okay, that's perfect. – Travis Feb 23 '15 at 19:19
  • Better make the method a strict mode function so that `this` is indeed a primitive value. `instanceof String` might not be reliable. – Bergi Jun 17 '15 at 05:20
4

To show u some example:

String.prototype.myFunction = function() {
    return this+"asd";
};

this function will add "asd" to each string when myFunction() is called.

var s = "123";
s = s.myFunction();
//s is now "123asd"
Travis
  • 1,274
  • 1
  • 16
  • 33
Cracker0dks
  • 2,422
  • 1
  • 24
  • 39
  • as "Dai" written above. In javascript there is no "Master Object" like in Java. So its better to use a normal function in this case (No prototype needed). If you have to do it, u have to define it for each data type. An other solution is to write a wrapper object wich hold the other different data types... but this not a good coding style and more work to read and write than a simple function. – Cracker0dks Feb 18 '15 at 01:47
  • @Cracker0dks There is a master object in javascript; it's just `Object`. See my answer :) –  Feb 18 '15 at 02:15
4

Before we start, few important statements to remember and be aware of (true for all string literal/primitive, String object, number literal/primitive, Number object etc.):

  • All objects in JavaScript are descended from Object and inherit methods and properties from Object.prototype – String, Number etc (much like Java).
  • JS has 6 primitive types – string, number, boolean, null, undefined and symbol
  • JS has their corresponding wrapper objects – String, Number, Boolean and Symbol
  • As you can see above, JS has string as a primitive as well an Object
  • Primitive is not of type Object.
  • String literal is a primitive and String object is of type Object.
  • The instanceof operator tests whether an object has in its prototype chain the prototype property of a constructor. (first condition to get TRUE here is that instanceof should be used against an Object or its subclass)
  • The typeof operator returns a string indicating the type of the unevaluated operand.

String as primitive:
String primitive or literal can be constructed in following ways:

var str1 = “Hello”;
var str2 = String(“Hello”);
typeof str1;                    //Output = "string"
typeof str2;                    //Output = "string"
str1 instanceof (String || Object)      //Output = false because it is a primitive not object
str2 instanceof (String || Object)      //Output = false because it is a primitive not object

String as Object:
String object can be constructed by calling its constructor from new object:

var str3 = new String(“Hello”);
typeof str3;        //Output = "string"
str3 instanceof (String)        //Output = true because it is a String object
str3 instanceof (Object)        //Output = true because it is an Object

Above all may look little obvious but it was necessary to set the ground.
Now, let me talk about your case.

Object.prototype.isString = function() {
    return typeof this === "string";
}
"5".isString() //returns false
"".isString()  //returns false
var str = "string"
str.isString() //returns false

You are getting FALSE as o/p because of concept called as Auto-boxing. When you call any method on string literal then it gets converted to String object. Read this from MSDN - “Methods for String Literals”, to be sure in yourself.

So, in your prototype when you will check type using typeof then it will never be a literal (typeof == "string") because it is already converted into an object. That's the reason you were getting false, if you will check typeof for object then you will get true, which I am going to talk in detail below:

  • typeof will give information on what type of entity it is - an object or a primitive (string, number etc) or a function.
  • instanceof will give information on what type of JS Object it is - Object or String or Number or Boolean or Symbol

Now let me talk about solution which is provided to you. It says to do a instanceof check, which is 100% correct, but with a note that upon reaching your prototype it could be of object type or function type. So, the solution which I am providing below will give you a picture of the same.

My recommendation is to have a generic function which would return you the type of instance, and then you can do whatever you want based on if it is a Number or String etc. isString is good but then you have to write isNumber etc., so instead have a single function which will return you the type of instance and can even handle function type.
Below is my solution:

Object.prototype.getInstanceType = function() {
    console.log(this.valueOf());
    console.log(typeof this);
    if(typeof this == "object"){
        if(this instanceof String){
            return "String";
        } else if(this instanceof Boolean){
            return "Boolean";
        } else if(this instanceof Number){
            return "Number";
        }  else if(this instanceof Symbol){
            return "Symbol";
        } else{
            return "object or array";   //JSON type object (not Object) and array
        }
    } else if(typeof this == "function"){
        return "Function";
    } else{
        //This should never happen, as per my knowledge, glad if folks would like to add...
        return "Nothing at all";
    }
}

Output:

new String("Hello").getInstanceType()               //String
"Hello".getInstanceType()               //String
(5).getInstanceType()               //Number
(true).getInstanceType()            //Boolean
Symbol().getInstanceType()          //Symbol
var ddd = function(){}
var obj = {}
obj.getInstanceType()               //object or array
var arr = []
arr.getInstanceType()               //object or array
ddd.getInstanceType()               //Function
($).getInstanceType()               //Function, because without double quotes, $ will treated as a function
("$").getInstanceType()             //String, since it came in double quotes, it became a String

To wrap up: Your 2 concerns as below

But what I need to be able to do is use that on any value, and access the value in the function.

You can access the value in your function using this. In my solution you can see console.log(this.valueOf());

Is there any workaround to having this be a property of any Object if I use Object.prototype?

You can achieve it from Object.prototype.getInstanceType as per above solution, and you can invoke it on any valid JS object and you will get the desired information.

Hope this helps!

hagrawal7777
  • 14,103
  • 5
  • 40
  • 70
  • Notice that there also are objects that do not descend from `Object`. – Bergi Jun 17 '15 at 05:16
  • @Bergi True, only instances of Object will descend from Object. JSON objects `(var obj = {};)` are object but do not descend from Object. – hagrawal7777 Jun 17 '15 at 17:58
  • No, that's not what I mean. Objects constructed by evaluating object literals do indeed inherit from `Object.prototype` as all others do. But objects like `Object.create(null)` don't. – Bergi Jun 17 '15 at 18:09
  • @Bergi Yes, because in JS "null" is a primitive and there is no wrapper object for the same. So, `Object.create(null) instanceof Object` will yield `false`. – hagrawal7777 Jun 18 '15 at 14:14
2

From the MDN Description of Object:

All objects in JavaScript are descended from Object

So, you can add methods to Object.prototype, which can then be called on anything. For example:

Object.prototype.isString = function() {
    return this.constructor.name === 'String';
}

console.log("hi".isString()); //logs true
console.log([].isString()); //logs false
console.log(5..isString()); //logs false

You could create this isX functions for each type of primitive there is, if you wanted. Either way, you can add methods to every type, since everything in JavaScript descends from an Object.

Hope that helps, and good luck :)

--edit--

I did want to point out that just because you can do this doesn't mean that you should. It's generally a bad practice to extend built-in functionality of JavaScript, even more so for a library that others will use. It depends on your use-case, though. Best of luck.

  • 1
    A good reason NOT to add properties this way to `Object.prototype` is that this will pick up the `isString` property in all iterations of any object as in `x = {}; for (prop in x) {}`, http://jsfiddle.net/jfriend00/5qLwa9L4/. This can easily break existing code. – jfriend00 Feb 18 '15 at 03:02
  • @jfriend00 is there a workaround you know of? I'm editíng the question now to ask for one. – Travis Feb 18 '15 at 03:55
  • 2
    @Wyatt - I have not tried myself, but you could probably use `Object.defineProperty()` on the prototype and create the property as `enumerable: false`. That still doesn't mean it's a good idea to extend the built in objects with your own custom methods that are not part of a standard. All popular frameworks have moved away from doing that for valid reasons. – jfriend00 Feb 18 '15 at 03:58
  • @jfriend00 I understand that this is potentially unsafe, just trying to find ways to make it safer and test if it will ever become safe for some level of standard use. – Travis Feb 18 '15 at 04:00
  • @Wyatt - it is not currently considered a good practice to extend the built-in objects in non-standard ways. – jfriend00 Feb 18 '15 at 04:02
  • 1
    @jfriend00 libraries such as underscore, lodash, or even jquery, rely on a generic function (`_` or `$` for instance) that returns a wrapper from an object. For instance if your library is called `toto`you could provide `toto(anything).extendedMethod()` – floribon Feb 18 '15 at 04:04
  • @floribon - yes, that is a common practice that is safe. You create your own object and you extend it. That is not what the OP is asking to do. – jfriend00 Feb 18 '15 at 04:05
  • -snip- refer to at @jfriend00's comment – Travis Feb 18 '15 at 04:06
2

Discussions aside, that this is not good practice and not a common approach like the wrapper-constructor, you should achieve this with either asking for the constructor's name:

Object.defineProperty(Object.prototype, 'isString', {
  value: function() { return this.constructor.name === "String"; }
});

or with the also already mentioned instanceof method:

Object.defineProperty(Object.prototype, 'isString', {
  value: function() { return this instanceof String; }
});

Explanation why your method didn't work is taking care of in this post.

If you want your new defined property to be enumerable, configurable or writable, you should take a look at the docs for defineProperty.

Community
  • 1
  • 1
Sebastian G. Marinescu
  • 2,374
  • 1
  • 22
  • 33
2

As a few others have pointed out your code is almost correct expect for the typeof this === 'string' part which doesn't work due to JavaScript's quirky behavior when it comes to primitives vs. objects. One of the most robust ways to test if an object is a string is with Object.prototype.toString.call(this) === '[object String]' (check out this article). With that in mind you could simply write your implementation of isString like so:

Object.prototype.isString = function () {
    return Object.prototype.toString.call(this) === '[object String]';
};

"abc".isString(); // ==> true
"".isString(); // ==> true
1..isString(); // ==> false
{}.isString(); // ==> false    
Ethan Lynn
  • 1,009
  • 6
  • 13
2

This is because string literals are of native string type, not actually an instance of String object so, in fact, you cannot actually call any method from Object or String prototype.

What is happening is that when you try to call any method over a variable which type is string, Javascript is automatically coercing that value to a newly constructed String object.

So

"abc".isString();

Is the same as:

(new String("abc")).isString();

The side effect of that is that what you are receiving in your isString() method is an (variable of type) Object which, also, is an instance of the String object.

Maybe you could see it more clearly with a simplified example:

var a = "Hello";
console.log(typeof a); // string

var b = new String("Hello");
console.log(typeof b); // object

By the way, the best chance you have to detect string in your function, as many others said, is to check if it is an instance of the String object with:

foo instanceof String

If you want to also check over other possible types, you should do a double check like the following:

function someOtherFn(ins) {
    var myType = typeOf ins;
    if (myType == "object" && ins instanceof String) myType = "string";
    // Do more stuf...
}
bitifet
  • 3,514
  • 15
  • 37