Backbone must rely on underscore.js to be used. It must use functions in underscore to complete the basic operations of accessing page elements and processing elements.
Note: backbone works well with other js libraries, so it is a library, not a framework.
Underscore does not extend the native object, but calls the _() method to encapsulate. Once the encapsulation is completed, the js object becomes an Underscore object. You can also obtain the data in the native js object through the Value() method of the Underscore object. (jquery obtains Jquery object through $() method)
Underscore has more than 60 functions in total. According to the different processing objects, it can be divided into five major modules: collection class, array class, functional function class, object class, and tool function class.
Underscore template() function description:
This function contains three templates:
(1) <% %>: Contains logical code and will not be displayed after rendering.
(2) <%= %>: Data type, display data after rendering.
(3) <%- %>: Convert HTML tags to common strings to avoid code attacks.
Call format:
_.template(templateString, [data], [setting])
No two-way data binding is implemented.
1. Underscore object encapsulation
Underscore does not extend in the native JavaScript object prototype, but encapsulates data in a custom object like jQuery (hereinafter referred to as "Underscore object").
You can get native JavaScript data by calling the value() method of an Underscore object, for example:
// Define a JavaScript built-in object var jsData = { name : 'data' } // Create the object as an Underscore object through the _() method// The prototype of the underscoreData object contains all the methods defined in Underscore. You can use var underscoreData = _(jsData); // Get native data through the value method, that is, jsData underscoreData.value();2. Priority call to JavaScript 1.6 built-in methods
There are many methods in Underscore that have been included in the specification in JavaScript 1.6. Therefore, within the Underscore object, built-in methods provided by the host environment (if the host environment has implemented these methods) will be called first to improve the execution efficiency of the function.
For host environments that do not support JavaScript 1.6, Underscore will implement them in its own way, and for developers, these are completely transparent.
The host environment mentioned here may be the Node.js running environment or the client browser.
3. Change the namespace
Underscore uses _ (underscore) by default to accessing and creating objects, but this name may not comply with our naming specifications or may easily cause naming conflicts.
We can change the name of Underscore through the noConflict() method and restore the value before the _(underscore) variable, for example:
<script type="text/javascript"> var _ = 'custom variable'; </script> <script type="text/javascript" src="underscore/underscore-min.js"></script> <script type="text/javascript"> // Underscore object console.dir(_); // Rename the Underscore object to us, and then access and create the Underscore object through us var us = _.noConflict(); // Output "custom variable" console.dir(_); </script>
4. Chain operation
Remember how we do link operations in jQuery? For example:
$('a') .css('position', 'relative') .attr('href', '#') .show();Underscore also supports chain operations, but you need to call the chain() method to declare:
var arr = [10, 20, 30]; _(arr) .chain() .map(function(item){ return item++; }) .first() .value();If the chain() method is called, Underscore will encapsulate the called method in a closure, encapsulate the return value into an Underscore object and return:
// This is a key function in Underscore to implement chain operations. It encapsulates the return value into a new Underscore object and calls the chain() method again to provide support for the next function in the method chain. var result = function(obj, chain) { return chain ? _(obj).chain() : obj; }5. Expand Underscore
We can easily extend custom methods into Underscore via mixin() method, for example:
_.mixin({ method1: function(object) { // todo }, method2: function(arr) { // todo }, method3: function(fn) { // todo } });These methods are appended to the Underscore prototype object, which can be used by all created Underscore objects, and they enjoy the same environment as other methods.
6. Traverse the collection
Each() and map() methods are the two most commonly used methods. They are used to iterate over a collection (array or object) and process each element in the collection in turn, for example:
var arr = [1, 2, 3]; _(arr).map(function(item, i) { arr[i] = item + 1; }); var obj = { first : 1, second : 2 } _(obj).each(function(value, key) { return obj[key] = value + 1; });The map() method has the same role and parameters as each() method, but it records the result returned by each iteration function to a new array and returns.
7. Function throttling
Function throttling refers to controlling the execution frequency or interval of a function (just like the gate that controls water flow). Underscore provides two methods: debounce() and throttle() for function throttling.
To describe these two methods more clearly, assume that we need to implement two requirements:
Requirement 1: When the user enters the search criteria in the text box, the matching keywords are automatically queryed and prompted to the user (just as when entering the search keyword in Tmall)
First, analyze the first requirement. We can bind the keypress event of the text box. When the content of the input box changes, query the matching keyword and display it. Suppose I want to query "windows phone", which contains 13 characters, and it only took 1 second for me to complete the input (it seems a bit fast, that's what it means), then within this 1 second, the query method was called 13 times. This is a very scary thing, and if Tmall is implemented like this, I'm worried that it will die before Singles' Day (of course, it's not that fragile, but it's definitely not the best solution)
A better approach is to query the matching keyword when the user has finished inputting or is waiting for a prompt (maybe he is too lazy to enter the following content).
Finally we found that in both cases we expected, the user will temporarily stop input, so we decided to query after the user pauses input for 200 milliseconds (if the user is constantly typing content, then we think he may be very clear about the keyword he wants, so wait and prompt him again)
At this time, using the debounce() function in Underscore, we can easily implement this requirement:
<input type="text" id="search" name="search" /> <script type="text/javascript"> var query = _(function() { // Query operation here}).debounce(200); $('#search').bind('keypress', query); </script>You can see that our code is very concise, and throttling control has been implemented in the debounce() method. We only tell it that if the query function has not been called within 200 milliseconds, we will execute our query operation, and then bind the query function to the keypress event in the input box.
How did the query function come about? When we call the debounce() method, we will pass a function that performs the query operation and a time (milliseconds). The debounce() method will throttling the function based on the time we pass, and return a new function (i.e. the query function). We can call the query function with confidence and boldness, and the debounce() method will help us control it as required.
Requirement 2: When the user drags the browser scrollbar, call the server interface to check whether there is new content
Let’s analyze the second requirement. We can bind the query method to the window.onscroll event, but this is obviously not a good practice, because the user drags the scrollbar once and may trigger dozens or even hundreds of onscroll events.
Can we use the above debounce() method to perform throttling control? When the user has finished dragging the scrollbar, then query for new content? But this is inconsistent with the requirements, and users hope to see changes in new content during the dragging process.
So we decided to do this: when the user drags, the interval between every two queries is no less than 500 milliseconds, if the user drags for 1 second, this may trigger 200 onscroll events, but we only do 2 queries at most.
Using the throttle() method in Underscore, we can also easily implement this requirement:
<script type="text/javascript"> var query = _(function() { // Query operation here}).throttle(500); $(window).bind('scroll', query); </script>The code is still very concise, because inside the throttle() method, all the controls we have implemented are already for.
You may have found that the two methods of debounce() and throttle() are very similar (including the call method and return value), but their functions are different.
They are all used for function throttling, and control functions are not called frequently, saving client and server resources.
The debounce() method focuses on the interval between function execution, that is, the call time of the function twice cannot be less than the specified time.
The throttle() method focuses more on the execution frequency of the function, that is, the function will only be called once within the specified frequency.
8. Template analysis
Underscore provides a lightweight template parsing function that helps us effectively organize page structure and logic.
I'll introduce it with an example:
<!-- Used to display rendered tags --> <ul id="element"></ul> <!-- Define a template and put the template content into a script tag --> <script type="text/template" id="tpl"> <% for(var i = 0; i < list.length; i++) { %> <% var item = list[i] %> <li> <span><%=item.firstName%> <%=item.lastName%></span> <span><%-item.city%></span> </li> <% } %> </script> <script type="text/javascript" src="underscore/underscore-min.js"></script> <script type="text/javascript"> // Get rendering elements and template content var element = $('#element'), tpl = $('#tpl').html(); // Create data, which may be the var data you get from the server = { list: [ {firstName: '<a href="#">Zhang</a>', lastName: 'San', city: 'Shanghai'}, {firstName: 'Li', lastName: 'Si', city: '<a href="#">Beijing</a>'}, {firstName: 'Wang', lastName: 'Wu', city: 'Guangzhou'}, {firstName: 'Zhao', lastName: 'Liu', city: 'Shenzhen'} ] } // parse the template, return the parsed content var html = _.template(tpl, data); // populate the parsed content to the rendering element element.html(html); </script>In this example, we put the template content into a <script> tag, you may have noticed that the tag's type is text/template instead of text/javascript, because it cannot be run directly as a JavaScript script.
I also recommend that you put the template content in <script> because if you write them in a <div> or other tags, they may be added to the DOM tree for parsing (even if you hide this tag, it will not be avoided).
_.template template function can only parse 3 types of template tags (this is much simpler than Smarty and JSTL):
<% %>: Used to contain JavaScript code that will be executed when the data is rendered.
<%= %>: Used to output data, it can be a variable, an object's attribute, or a function call (to output the return value of the function).
<%- %>: Used to output data, and convert HTML characters contained in the data into entity forms (for example, it converts double quotes to form), to avoid XSS attacks.
When we want to display HTML in the data as text, we often use the <%- %> tag.
Underscore also allows you to modify these 3 forms of tags. If we want to use {% %}, {%= %}, {%- %} as tags, we can do it by modifying templateSettings, just like this:
_.templateSettings = { evaluation : //{%([/s/S]+?)/%/}/g, interpolate : //{%=([/s/S]+?)/%/}/g, escape : //{%-([/s/S]+?)%/}/g }In this example, we pass the template content and data to be populated to the template method, which will be processed in the following order:
(1) Parsing the template content into executable JavaScript (parse template tags)
(2) Modify the parsed JavaScript scope to the data object we pass through with statement, which allows us to directly access the properties of the data object through variables in the template.
(3) Execute parsed JavaScript (filling data into template)
(4) Return the result after execution
We often encounter a situation where the template method is called multiple times to render the data to the same template.
Suppose we have a paging list, and each piece of data in the list is rendered through a template. When the user enters the next page, we will get the data of the next page and re-render. In fact, the template is the same every time it is rendered, but all the processing processes of the template described just now will always be executed.
In fact, the template method of Underscore provides a more efficient way of calling. We modify the last two sentences in the above code to:
// parse the template, return the parsed content var render = _.template(tpl); var html = render(data); // populate the parsed content to the render element element.html(html);
You will find a subtle difference: when we call the template method, we only pass the template content, but not the data. At this time, the template method will parse the template content, generate the parsed executable JavaScript code, and return a function, and the function body is the parsed JavaScript. Therefore, when we call this function to render data, we omit the template parsing action.
You should store the returned function (just like I store it in the render variable), and then render the data by calling the function, especially if the same template may be rendered multiple times. Doing so can improve execution efficiency (the specific improvement should depend on the length and complexity of your template, but it is a good habit anyway).