5 interesting JavaScript finds made in Vue source code

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 Mapor 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 argor in the prototypeargthese 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()  // [object Object]
_toString.call(obj) // [object Object]

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()  // 111
_toString.call(obj) // [object Object]
/hello/.toString() // /hello/
_toString.call(/hello/) // [object RegExp]

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 standard

Looking 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) // "Null"
toRawType(/sdfsd/) //"RegExp"

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, cachedyou can cache the results of previous calls to the target function using the entity Objector 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-worldlines to view lines helloWorld.

const camelizeRE = /-(\w)/g
const camelize = cached((str) => {
  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})
camelize('hello-world')
// "helloWorld"

β†’ 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 isArray() { [native code] }"
function fn(){} 
fn.toString() // "function fn(){}"

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?


All Articles