Reading the source code of well-known frameworks can help a programmer to improve his professional skills. The author of the article, the translation of which we are publishing today, recently analyzed the vue2.x code. He found in this code some interesting JavaScript ideas that he decided to share with everyone.
1. Determining the exact type of any object
As we all know, in JavaScript there are six primitive data types ( Boolean
, Number
, String
, Null
, Undefined
, Symbol
), and one object type - Object
. Do you know how to distinguish between types of different object values? An object can be an array or a function, it can be a collection of values Map
or something else. What needs to be done in order to find out the exact type of object?Before looking for an answer to this question, letβs think about the difference between Object.prototype.toString.call(arg)
and String(arg)
.The use of these expressions is aimed at converting the parameter passed to them into a string. But they work differently.When called, the String(arg)
system will try to call arg.toString()
or arg.valueOf()
. As a result, if in arg
or in the prototypearg
these methods will be overwritten, calls Object.prototype.toString.call(arg)
and String(arg)
will give different results.Consider an example.const _toString = Object.prototype.toString
var obj = {}
obj.toString()
_toString.call(obj)
We will execute this code in the browser developer tool console.In this case calls obj.toString()
and Object.prototype.toString.call(obj)
lead to the same results.Here is another example.const _toString = Object.prototype.toString
var obj = {}
obj.toString = () => '111'
obj.toString()
_toString.call(obj)
/hello/.toString()
_toString.call(/hello/)
Let's execute the code in the console.Now, calling the method of the object .toString()
and using the construct Object.prototype.toString.call(obj)
give different results.Here are the rules that describe the behavior of a method in the ECMAScript standard Object.prototype.toString()
.Description of the Object.prototype.toString () method in the ECMAScript standardLooking at the documentation, we can conclude that when calledObject.prototype.toString()
for different objects, different results will be returned.Explore this idea in the console.In addition, the value returned Object.prototype.toString()
is always presented in the following format:β[object β + βtagβ +β] β
If we need to extract only a part from this construction tag
, we can get to this part by removing unnecessary characters from the beginning and end of the line using a regular expression or method String.prototype.slice()
.function toRawType (value) {
const _toString = Object.prototype.toString
return _toString.call(value).slice(8, -1)
}
toRawType(null)
toRawType(/sdfsd/)
Explore this feature in the console.As you can see, using the above function you can find out the exact type of an object variable.β Here , in the Vue repository, you can find the code for a similar function.2. Caching the results of the function
Suppose there is a function similar to the following that performs lengthy calculations:function computed(str) {
console.log('2000s have passed')
return 'a result'
}
By creating such a function, we intend to cache the results returned to it. When this function is called next time, passing it the same parameters as before, the "heavy" function code will not be executed. Instead, the cached result will be returned, without any extra time. How to do it?You can, for example, write a wrapper function for an objective function. Such a function can be given a name cached
. This function accepts, as an argument, the objective function, and returns a new function equipped with caching capabilities. In the function, cached
you can cache the results of previous calls to the target function using the entity Object
or Map
. Here is the code for this function:function cached(fn){
const cache = Object.create(null);
return function cachedFn (str) {
if ( !cache[str] ) {
let result = fn(str);
cache[str] = result;
}
return cache[str]
}
}
Here is an example of using the above function.β Here is the code for a similar function that is available in the Vue code base.3. Convert a string of the form hello-world to a string of the form helloWorld
When several programmers work together on the same project, it is very important for them to take care of the uniform style of the code. Someone, for example, can record some composite identifiers in a format helloWorld
, and someone in a format hello-world
. In order to bring order to this area, you can create a function that converts view hello-world
lines to view lines helloWorld
.const camelizeRE = /-(\w)/g
const camelize = cached((str) => {
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})
camelize('hello-world')
β This is the place of the Vue code where this example comes from.4. Determining which environment the JavaScript code runs in
Nowadays, given the rapid development of browsers, JavaScript code can be executed in various environments. In order to better adapt projects to different environments, you need to be able to determine where the programs are executed:const inBrowser = typeof window !== 'undefined'
const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform
const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase()
const UA = inBrowser && window.navigator.userAgent.toLowerCase()
const isIE = UA && /msie|trident/.test(UA)
const isIE9 = UA && UA.indexOf('msie 9.0') > 0
const isEdge = UA && UA.indexOf('edge/') > 0
const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android')
const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios')
const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge
const isPhantomJS = UA && /phantomjs/.test(UA)
const isFF = UA && UA.match(/firefox\/(\d+)/)
β This is where I found this code.5. Distinction between built-in and user-defined functions
JavaScript is known to have two kinds of functions. The first type is built-in, or, as they are also called, "native" functions. Such functions are provided by the environment in which the code is executed. The second type is the so-called "user functions", that is, those that programmers write themselves. You can distinguish between these functions, taking into account the fact that, when converting them to strings, various results are returned.Array.isArray.toString()
function fn(){}
fn.toString()
Weβll experiment with this code in the console.The toString()
native function method always returns a construction of the following form:function fnName() { [native code] }
Knowing this, you can write a function that allows you to distinguish between native and user-defined functions:function isNative (Ctor){
return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
}
β This is the place in the Vue code base where there is such a function.Did you manage to find something interesting by exploring the code in the repositories of famous JavaScript projects?