In this chapter, we will look at how to create polymorphic functions, that is, those that handle arguments differently, depending on their type. For example, the output function can format numbers and dates differently.
To implement this, you need a way to determine the type of a variable. And here in JavaScript there is a whole "zoo" of ways, but we'll figure it out now.
As we know, there are several primitive types :
-
null
- Special type, contains only
null
. -
undefined
- Special type, contains only the value
undefined
. -
number
- Numbers:
0
, 3.14
, as well as NaN
and Infinity
values -
boolean
-
true
, false
. -
string
- Strings, such as
"Мяу"
or the empty string ""
.
All other values are objects , including functions and arrays.
typeof
operator
The typeof
operator returns the type of the argument. It has two syntaxes:
- Operator syntax:
typeof x
. - Function syntax:
typeof(x)
.
They work the same, but the first syntax is shorter.
The result of typeof
is a string containing the type:
The last two lines are marked, because typeof
behaves in them in a special way.
- The result of
typeof null == "object"
is an officially recognized error in the language, which is stored for compatibility. Actually, null
is not an object, but a primitive. This is immediately apparent if you try to assign a property to it:
- For function
f
value of typeof f
is "function"
. Of course, the function is an object. On the other hand, such a selection of functions in practice is rather a plus, since makes it easy to define a function.
In the old code, you can sometimes see a code like this:
if ( typeof jQuery !== 'undefined' ) { |
Its author apparently wants to check if the
jQuery
variable exists. Moreover, he is referring to the global
jQuery
variable, which is created in the external script, because he knows everything about his local ones.
A shorter if (jQuery)
code will generate an error if the variable is not defined, and typeof jQuery
does not generate an error in such cases, but returns undefined
.
But just here typeof
not needed! There is another way:
1 | if (window.jQuery !== undefined ) { ... } |
4 | if (window.jQuery) { ... } |
When accessing a global variable through a
window
there will be no error, because by syntax this is a call to the property of the object, and such calls if the property is missing simply return
undefined
.
The typeof
operator works reliably with primitive types other than null
, as well as with functions. But ordinary objects, arrays and dates for typeof
all look the same, they are of type 'object'
:
Therefore, it is impossible to distinguish them using typeof
.
[[Class]]
for embedded objects
The main typeof
problem is the inability to distinguish objects other than functions. But there is another way to get the type.
All embedded objects have a hidden [[Class]]
property. It is equal to "Array"
for arrays, "Date"
for dates, etc.
This property cannot be obtained directly, there is a trick to read it.
The fact is that toString
from a standard object prints [[Class]]
in a small wrapper. For example:
Here, inside [object ...]
indicated just the value of [[Class]]
, which for an ordinary object is exactly "Object"
. For dates it will be Date
, for arrays - Array
, etc.
Most built-in objects in JavaScript have their own toString
method. Therefore, we will use a technique called “method borrowing” ( “method borrowing” ).
We will take the toString
function from the standard object and run it in the context of those values for which we need to get the type:
Let us analyze what is happening in more detail.
- You can rewrite this line in two:
var toClass = obj.toString; |
In other words, we create an empty object {}
and copy the reference to its toString
method into the variable toClass
.
We do this because the internal toString
implementation of the standard Object
returns [[Class]]
. Other objects ( Date
, Array
, etc.) have their own toString
for this purpose as well.
- Call the copied method in the context of the required object
obj
. We could do better:
... But why copy an extra property into an object? The syntax toClass.call(arr)
does the same, so we use it. - All class received. If desired, you can remove the
[object ...]
wrapper by taking a substring by calling slice(8,-1)
.
The method also works with primitives:
... But without use strict
calling the call
with arguments null
or undefined
passes this = window
. This is the behavior of the old JavaScript standard.
This method can only give type for embedded objects. For custom constructors, always [[Class]] = "Object"
:
When testing the code in the console, you may find that if you enter {}.toString.call(...)
on the command line, there will be an error. On the other hand, the alert( {}.toString... )
call alert( {}.toString... )
- works.
This error occurs because the curly braces { }
in the main code stream are interpreted as a block. The interpreter reads {}.toString.call(...)
like this:
Curly brackets are considered an object only if they are in the context of an expression. In particular, wrapping parentheses
( {}.toString... )
will also work fine.
Duck typing
Duck typing is based on one well-known proverb: "If it looks like a duck, then it really is a duck . "
Translated: “If it looks like a duck, swims like a duck and quacks like a duck, then it’s probably a duck (what a difference it really is) . ”
The meaning of duck typing is to test methods and properties, regardless of the type of the object.
We can check the array by specifying the presence of the splice
method:
Note that in if(x.splice)
we do not call the x.splice()
method, but try to get the x.splice
property x.splice
. For arrays, it always exists and is a function, i.e. will give a true
logical context.
You can check for the date by checking the availability of the getTime
method:
It looks fragile, it can be broken by passing a similar object with the same method. But in practice, duck typing works well and stably, especially when it is not the type itself that is important, but the support of the methods.
Type checking for user objects
To check who created the object, there is an instanceof
operator.
Syntax: obj instanceof Func
.
For example:
The instanceof
operator also works for embedded objects:
The instanceof
operator can only perform a check, it does not allow to get the type as a string, but in most cases this is not required.
As we have seen, instanceof
successfully works with embedded objects:
... However, there is a case when this method will let us down. Namely, if the object is created in another window or iframe
, and from there it is transferred to the current window.
In this case, the arr instanceof Array
returns false
, since Each window and frame has its own set of embedded objects. The arr
array is Array
in the context of that window
.
The [[Class]]
method is free from this flaw.
Polymorphism
We use type checking to create the polymorphic function sayHi
.
It will work in three modes:
- Without arguments: displays
Привет
. - With an argument that is not an array: displays “hello” and this argument.
- With an argument that is an array, it says hello to everyone.
An example of such a function:
Pay attention, even support of nested arrays was obtained
Total
There are two ways to get a type :
-
typeof
- Good for primitives and functions, lying about
null
. -
[[Class]]
property - You can get it using
{}.toString.call(obj)
. This property contains the type for embedded objects and primitives, except for null
and undefined
.
There are two more ways to check types :
- Duck typing
- You can check the method support.
-
instanceof
operator - Works with any objects that have been built in and created by the visitor using constructors:
if (obj instanceof User) { ... }
.
Type checking is used, as a rule, to create polymorphic functions, that is, those that work differently depending on the type of argument.
Importance: 5
For the definition of the primitive type string / number suitable operator typeof.
Examples of his work:
The typeof
operator does not know how to distinguish between different types of objects, they all look the same to him: "object"
. Therefore, it cannot distinguish Date
from Array
.
Use to distinguish between them, the [[Class]]
property.
Function:
[Open task in new window]
Importance: 2
- Yes. Since
undefined == null
, string (1)
will return to return false
. - If you remove the line
(1)
, then browsers behave more interesting ... In theory, if the lax mode, then the f.call(null/undefined)
call should pass to f
global object as the context of this
. So the browser should start the {}.toString
method in the context of the window
. And further - the result depends on what its [[Class]]
property is.
In reality, most browsers use a modern standard here and return [object Undefined]
. Try it yourself ..
Comments
To leave a comment
Scripting client side JavaScript, jqvery, BackBone
Terms: Scripting client side JavaScript, jqvery, BackBone