Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to write maintainable JavaScript code

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly shows you "how to write maintainable JavaScript code", the content is easy to understand, clear, hope to help you solve your doubts, the following let the editor lead you to study and learn "how to write maintainable JavaScript code" this article.

JavaScript has become very popular since the development of the programming language, and all kinds of nouns have emerged one after another. We can list a lot of them casually, such as Node.js, jQuery, JavaScript MVC, Backbone.js, AMD, CommonJS, RequireJS, CoffeScript, Flexigrid, Highchart, Script Loader, Script Minifier, JSLint, JSON, Ajax. There are so many things sweeping through our minds that it undoubtedly makes people dizzy. But the essence is always the same, and the so-called essence is some core basic concepts. The foundation here does not refer to the basic knowledge of JavaScript expressions, data types, function API, etc., but the foundation that supports the things behind such a lot of JavaScript nouns above. I know this will make it difficult for me to write this article, because it will contain too many topics, so this article is just going to get a glimpse: this article will first talk about some concepts, then some practical guidelines, and * * will involve a discussion of some tools.

Before we officially start this blog, we need to ask ourselves why code maintainability deserves our attention. I believe that as long as you have written a considerable amount of code, you have found this: Fix Bug is much more difficult than writing code. It's not uncommon to spend three hours writing code and then spend two or three days on one of the Fix's Bug. This is compounded by the fact that the people in Fix Bug are probably not the original authors of the code. Therefore, code maintainability is a topic worth discussing. To a certain extent, improving code maintainability can save Fix Bug time, save Fix Bug time and thus save labor costs.

No 1. Organize the code into modules

Basically any programming language believes that modularity can improve code maintainability. We know that the core of software engineering is to control complexity, while modularization is essentially to separate concerns and thus decompose complexity.

IIFE module mode

When we first learn to write JavaSript code, we almost always write code like this:

Var myVar = 10; var myFunc = function () {/ /...}

There is nothing wrong with such code itself, but when there is more and more such code, it will put a heavy burden on code maintenance. The reason is that this causes myVar and myFunc to be exposed to the global namespace, thus polluting the global namespace. In my personal experience, you usually start to think about this when there are about 200 lines of JavaScript code on a page, especially in enterprise projects. So what are we going to do?

The simplest solution is to use IIFE (Immediate Invoked Function Expression, execute the function expression immediately) to solve the problem (note that this is a function expression, not a function declaration, which is similar to var myFunc = function () {/ /.}), as follows:

(function () {var myVar = 10; var myFunc = function () {/ /...};}) ()

Now the scope of myVar and myFunc is locked inside this function expression without polluting the global namespace. This is somewhat similar to the "sandboxie mechanism" (which also provides a secure execution context). We know that there is no block-level scope in JavaScript, and scopes can only be generated by functions, as in the above example.

But now that myVar and myFunc can only be used inside function expressions, what if it needs to provide some excuse or functionality (like most JavaScript frameworks or JavaScript libraries)? We will adopt the following approach:

(function (window, $, undefined) {var myFunc = function () {/ /...} window.myFunc = myFuc;}) (window, jQuery)

Let's make a simple analysis, and the code is simple: first, take the window object and the jQuery object as parameters to immediately execute the function expression, and $is just an alias for the incoming jQuery object. Second, we did not pass a third parameter, but the function has a parameter named undefined, which is a trick. Precisely because the third parameter is not passed, the value of the third parameter undefined is always undefined, which ensures that you can rest assured to use undefined internally without having to worry about modifying the value of undefined elsewhere; * the function to be exposed to the outside through window.myFunc.

For example, if we look at an example of an actual JavaScript class library, such as Validate.js, we can see that it derives the function as follows:

(function (window, document, undefined) {var FormValidator = function (formName, fields, callback) {/ /...}; window.FormValidator = FormValidator;}) (window, document)

Is it basically the same as what I said before? Another example is one of the writing paradigms for jQuery plug-ins, as follows:

(function ($) {$.fn.pluginName = function () {/ / plugin implementation code};}) (jQuery)

Now that the jQuery plug-ins are here, it wouldn't hurt to have another example of the jQuery source code:

(function (window, undefined) {var jQuery = function (selector, context) {/ / The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init (selector, context, rootjQuery)}, / / Expose jQuery to the global object window.jQuery = window.$ = jQuery;}) (window)

This allows us to call the jQuery function with either $("body") or jQuery ("body").

Namespace (Namespace)

Although the use of the IIEF module pattern allows our code to be organized into modules and improves maintainability, if the size of the code increases further, such as reaching a level of 2000-10000, the limitations of the previous approach will be reflected again?

It's hard to say? Looking at the previous code, all functions are exported as properties of window objects, which makes it less elegant if there are many developers working at the same time. In particular, there may be a hierarchical relationship between modules and modules, so we need to use "namespaces", which can be used to group functions.

We can write like this:

(function (myApp, $, undefined) {/ /...}) (window.myApp = window.myApp | | {}, jQuery)

Or this:

Var myApp = (function (myApp, $, undefined) {. Return myApp;}) (window.myApp | | {}, jQuery)

Now instead of passing the window object to the immediately executed function expression, we pass the namespace object mounted on the window object. The | | in the second section of the code is to avoid repeatedly creating objects when using the myApp variable in multiple places.

Revealing Module Pattern

The main function of this module mode is to distinguish between private variables / functions and public variables / functions, so as to hide private variables / functions inside the function and expose public variables / functions to the outside.

The code example is as follows:

Var myModule = (function (window, $, undefined) {var _ myPrivateVar1 = "; var _ myPrivateVar2 ="; var _ myPrivateFunc = function () {return _ myPrivateVar1 + _ myPrivateVar2;}; return {getMyVar1: function () {return _ myPrivateVar1;}, setMyVar1: function (val) {_ myPrivateVar1 = val;}, someFunc: _ myPrivateFunc};}) (window, jQuery)

MyPrivateVar1 and myPrivateVar2 are private variables, and myPrivateFunc is private functions. GetMyVar1 (public getter), getMyVar1 (public setter), and someFunc are public functions. Is it a bit like an ordinary Java Bean?

Or we can write it in this form (change soup without changing clothes):

Var myModule = (function (window, $, undefined) {var my= {}; var _ myPrivateVar1 = ""; var _ myPrivateVar2 = ""; var _ myPrivateFunc = function () {return _ myPrivateVar1 + _ myPrivateVar2;}; my.getMyVar1 = function () {return _ myPrivateVar1;}; my.setMyVar1 = function (val) {_ myPrivateVar1 = val;}; my.someFunc = _ myPrivateFunc Return my;}) (window, jQuery)

Module extension (Module Augmentation)

Sometimes we want to add additional functionality to an existing module, like this:

Var MODULE = (function (my) {my.anotherMethod = function () {/ / added method... }; return my;} (MODULE | | {}))

Tight Augmentation

The MODULE passed in in the above example may be undefined, which means it may not exist before. The corresponding Tight Augmentation pattern requires that the incoming MODULE must exist and have already been loaded.

Var MODULE = (function (my) {var old_moduleMethod = my.moduleMethod; my.moduleMethod = function () {/ / method override, has access to old through old_moduleMethod... }; return my;} (MODULE))

The intention of the code is clear: it implements the moduleMethod function that overrides the original module, and you can call od_moduleMethod in the rewritten function. But this is not flexible enough because it assumes a prerequisite: the MODULE module must exist and be loaded, and it contains moduleMethod functions.

Sub-module mode

This pattern is very simple. For example, we create a submodule for the existing module MODULE as follows:

MODULE.sub = (function () {var my = {}; / /. Return my;} ()

No 2. Using OO

Constructor mode (Constructor Pattern)

JavaScript doesn't have the concept of a class, so we can't create objects through classes, but we can create objects through functions. Such as the following:

Var Person = function (firstName, lastName, age) {this.firstName = firstName; this.lastName = lastName; this.age = age;}; Person.prototype.country = "China"; Person.prototype.greet = function () {alert ("Hello, I am" + this.firstName + "" + this.lastName);}

Here firstName, lastName, and age can be compared to instance variables in the Java class, and each object has its own copy. The country can be compared to the static variable in the Java class, and the greet function can be compared to the static method in the Java class, and all objects share a share. Let's verify it with the following code (enter it on the console of Chrome):

Var Person = function (firstName, lastName, age) {this.firstName = firstName; this.lastName = lastName; this.age = age;}; Person.prototype.country = "China"; Person.prototype.greet = function () {alert ("Hello, I am" + this.firstName + "" + this.lastName);}; var p1 = new Person ("Hub", "John", 30); var p2 = new Person ("Mock", "William", 23) Console.log (p1.fistName = = p2.firstName); / / false console.log (p1.country = = p2.country); / / true console.log (p1.greet = = p2.greet); / / true

But if you continue to test the following code, you won't get the p2.country you might expect to become UK:

P1.country = "UK"; console.log (p2.country); / / China

This is related to the scope chain, which I will elaborate on later. Keep coming back here. Now that the class can be simulated by function, how can we simulate the inheritance of the class?

For example, we now need a driver class to inherit Person. We can do this:

Var Driver = function (firstName, lastName, age) {this.firstName = firstName; this.lastName = lastName; this.age = age;}; Driver.prototype = new Person (); / / 1 Driver.prototype.drive = function () {alert ("Ihumm driving.");}; var myDriver = new Driver ("Butter", "John", 28); myDriver.greet (); myDriver.drive ()

Line 1 is the key to inheritance, after which Driver defines its own extended function drive, so that it can call both the greet function inherited from Person and its own drive function.

No3. Follow some practical guiding principles

The following is an incomplete summary of some practical principles that guide writing highly maintainable JavaScript code.

Try to avoid global variables

JavaScript uses functions to manage scopes. Each global variable becomes a property of the Global object. You may not be familiar with Global objects, so let's talk about Global objects first. The Global object in ECMAScript is defined in a sense as a "pocket" object: that is, all properties and methods that do not belong to any other object are ultimately its properties and methods. All variables and functions defined in the global scope are properties of the Global object. Things like escape (), encodeURIComponent (), and undefined are all methods or properties of the Global object.

In fact, one object that we are more familiar with points to the Global object, which is the window object. The following code demonstrates defining and accessing global objects:

Myglobal = "hello"; / / antipattern console.log (myglobal); / / "hello" console.log (window.myglobal); / / "hello" console.log (window ["myglobal"]); / / "hello" console.log (this.myglobal); / / "hello"

The disadvantages of using global variables are:

Global variables are shared by all code in the application, so it is easy to cause naming conflicts between different pages (especially if they contain third-party code)

Global variables may conflict with variables of the host environment

Function sum (x, y) {/ / antipattern: implied global result = x + y; return result;}

Result is now a global variable. It is also very simple to correct, as follows:

Function sum (x, y) {var result = x + y; return result;}

In addition, global variables created by var declarations are different from those implicitly created by var declarations in the following ways:

Global variables created by var declaration cannot be delete

Global variables created implicitly can be used by delete

After operation, the delete operator returns true or false to identify whether the deletion is successful, as shown below:

/ / define three globals var global_var = 1; global_novar = 2; / / antipattern (function () {global_fromfunc = 3; / / antipattern} ()); / / attempt to delete delete global_var; / / false delete global_novar; / / true delete global_fromfunc; / / true / / test the deletion typeof global_var; / / "number" typeof global_novar; / / "undefined" typeof global_fromfunc; / / "undefined"

Single Var Pattern is recommended to avoid global variables as follows:

Function func () {var a = 1, b = 2, sum = a + b, myobject = {}, I, j; / / function body... }

The above only uses a var keyword to make a, b, sum and other variables all local variables. And an initial value is set for each variable, which avoids possible logic errors in the future and improves readability (setting the initial value means that you can quickly tell whether the variable holds a numeric value, a string, or an object).

Another advantage of local variables over global variables is performance, and it is undoubtedly faster to find a variable from the local scope of a function inside a function than to find a global variable.

Avoid variable promotion (hoisting) traps

You've probably seen the following code many times, which is often used to examine the concept of variable promotion:

MyName = "global"; function func () {console.log (myName); / / undefined var myName = "local"; console.log (myName); / / local} func ()

What does this code output? The variable promotion of JavaScript makes this code equivalent to the following code:

MyName = "global"; function func () {var myName; console.log (myName); / / undefined myName = "local"; console.log (myName); / / local} func ()

So it is not difficult to understand that the output is undefined and local. Variable promotion is not an ECMAScript standard, but it is widely adopted.

Use for-in for non-array objects and ordinary for loops for logarithmic objects

Although technically for-in can traverse the properties of any object, for-in is not recommended for array objects for the following reasons:

If the array object contains an extension function, it may result in a logic error

For-in cannot guarantee the output order.

For-in traverses array objects with poor performance because it looks up the prototype chain for attributes on the prototype object it points to.

Therefore, it is recommended to use a normal for loop for array objects and for-in for non-array objects. However, when using for-in with non-array objects, you often need to use hasOwnProperty () to filter out attributes inherited from the prototype chain (rather than what you want to list), such as the following example:

/ the object var man = {hands: 2, legs: 2, heads: 1}; / / somewhere else in the code / / a method was added to all objects if (typeof Object.prototype.clone = "undefined") {Object.prototype.clone = function () {};} for (var i in man) {console.log (I, ":", man [I]);}

The output is as follows:

Hands: 2 legs: 2 heads: 1 clone: function () {}

That is, the addition of clone, which may be a function defined by another developer on the prototype object of Object, affects our current code, so the specification has two points. It is absolutely not allowed to extend functions or properties on the prototype object of the native object. Second, rewrite the code to something like this:

For (var i in man) {if (man.hasOwnProperty (I)) {console.log (I, ":", man [I]);}}

Further, we can rewrite the code as follows:

For (var i in man) {if (man.hasOwnProperty (I)) {console.log (I, ":", man [I]);}}

What's the advantage of this? The * point prevents man objects from overriding the hasOwnProperty function; the second is an improvement in performance, mainly because prototype chain lookups are faster.

Further cache the Object.prototype.hasOwnProperty function, and the code looks like this:

Var I, hasOwn = Object.prototype.hasOwnProperty; for (i in man) {if (hasOwn.call (man, I)) {/ / filter console.log (I, ":", man [I]);}

Avoid implicit type conversion

Implicit type conversions can lead to subtle logic errors. We know that the following code returns true:

0 = = false 0 = ""

The recommended practice is to always use constant and non-equal, that is, = and! =.

For the following code:

Null = = false undefined = = false

We often expect it to return true, but it returns false.

Then we can use the following code to cast it to a Boolean type and compare it:

!! null = false!! undefined = false

Avoid eval ()

Eval () accepts an arbitrary string and executes it as JavaScript code, initially often used to execute dynamically generated code, but eval () is harmful, such as causing XSS vulnerabilities. If you access a property value based on a variable attribute name, you can replace eval () with [], as follows:

/ / antipattern var property = "name"; alert (eval ("obj." + property)); / / preferred var property = "name"; alert (obj [property])

Note that passing strings to setTimeout (), setInterval (), and Function () is similar to eval () and should be avoided. Such as the following:

/ / antipatterns setTimeout ("myFunc ()", 1000); setTimeout ("myFunc (1,2,3)", 1000); / / preferred setTimeout (myFunc, 1000); setTimeout (function () {myFunc (1,2,3);}, 1000)

If you encounter scenarios where you have to use eval (), use new Function () instead, because even if a variable is declared through var in the string parameter of eval (), it will become a global variable, while new Function () will not, as follows:

Eval ("var myName='jxq'")

Then myName becomes a global variable, with newFunction () as follows:

Var a = new Function ("firstName, lastName", "var myName = firstName+lastName")

Actually an is now an anonymous function:

Function anonymous (firstName, lastName) {var myName = firstName+lastName}

Then myName is not a global variable now. Of course, if you insist on using eval (), you can wrap eval () with a function expression that executes immediately:

(function () {eval ("var myName='jxq';");}) (); / / jxq console.log (typeof myName); / / undefined

Another difference between eval () and Function () is that the former affects the scope chain, while the latter does not, as follows:

(function () {var local = 1; eval ("console.log (typeof local);");}) (); / / number (function () {var local = 1; Function ("console.log (typeof local);");}) (); / / undefined

When using parseInt (), specify the second binary parameter

There's no need to mention this. I'm sure everyone knows about it.

Using a script engine, let JavaScript parse the data to generate HTML

The legendary 12306 returns the following list when checking tickets (I can no longer complain. I just cut it today. In fact, it's about 100 lines):

G135

South of Beijing

12:40

Shanghai Hongqiao

18Rose 04jor05purl 24jue 8je Murray, yes, yes,--, pre-booking\ n1jue G137

South of Beijing

12:45

Shanghai Hongqiao

Why not just return the data (such as using JSON) and then parse the data using the JavaScript template engine? Such as the following (using the jQuery tmpl template engine, refer to my code JavaScript template engine for more details):

JavaScript tmpl Use Demo / / data used to populate the template var users = [{url: "http://baidu.com", name:" jxq "} {url: "http://google.com", name:" william "},] $(function () {/ / call template engine function to populate the template to get the final content $("# myUl") .html (tmpl ("user_tmpl", users));})

Using a template engine, you can completely separate data from HTML content, which has several benefits:

When you modify the HTML structure, you can hardly modify the structure of the returned data.

Only pure data is returned, saving network bandwidth (network bandwidth is money)

Adopt a consistent naming convention

The constructor initials are capitalized.

Rather than the lowercase of the constructor, it indicates that they should not be called through the new operator.

Constant names should be capitalized.

Private variables or function names may be preceded by an underscore, as follows:

Var person = {getName: function () {return this._getFirst () +'+ this._getLast ();}, _ getFirst: function () {/ /...}, _ getLast: function () {/ /...}}

Don't be stingy with notes, but don't make random comments either.

Add comments to some relatively difficult code, such as algorithm implementation.

Add comments to the function's functions, parameters, and return values.

Don't comment on some common sense code, and don't comment like this:

Var myName = "jxq"; / / declare the string variable myName, whose value is "jxq"

No4. Rational and efficient use of tools

The tools here include the JavaScript framework, the JavaScript class library, and some Code Snippet accumulated by yourself.

The advantage of using the JavaScript framework is that the framework provides us with a reasonable way to organize the code, such as Backbone.js and Knockout.js, which allows us to better separate the code according to MVC or MVP patterns.

Using the JavaScript class library avoids repeating wheels (and often creates some not-so-good wheels) and allows us to focus more on the overall business process than on the specific implementation of a function. Some general functions such as date processing, amount numerical processing * use the existing mature class library.

* using our own accumulated Code Snippet can improve our coding efficiency, and most importantly, it can provide a variety of reference solutions.

Here is a list of some popular tools.

JQueryCoreUISelect

It provides full customization, support for option groups, callback functions, and so on. Another plug-in to extend the Select control is jQuery Chosen. You can refer to the code I shared: beautify the Select drop-down box.

Sisyphus.js

It provides the form offline storage function, which can automatically save the form data that the user has not submitted. The data is cleared when the form is submitted.

TextExt

This class library allows us to convert HTML text into input fields.

Validate.js

This is a lightweight form validation class library that predefines a series of validation rules (through regular expressions), supports custom validation callback functions and validation failure messages, and is compatible with all major browsers (including IE 6). For more detailed information, please refer to my blog Validate.js framework source code for complete interpretation.

JQuery File Upload

JQuery file upload plug-in, which supports multiple file uploads

Handsontables: Excel-Like Tables For The Web

JQuery plug-ins that provide Web Excel functionality

Pivot.js

Through Pivot, we can easily display a large amount of data. The data source can be CSV or JSON

Date.js

A very convenient date processing class library.

It is easy to use. Here are two small examples:

/ / What date is next thursday? Date.today (). Next (). Thursday (); / / Add 3 days to Today Date.today (). Add (3). Days ();

RequireJS

RequireJS is a JavaScript file and module loader. Using RequireJS can significantly improve the efficiency of your code. It is said that the loading speed of Baidu music box has been improved by several seconds (on-demand loading) after using RequireJS, which is called an artifact.

Grunt.js

Grunt.js is a task-based command-line tool that can be used to build JavaScript projects. It pre-contains dozens of built-in tasks: file merging, project scaffolding (based on a predefined template), JSLint validation, UglifyJS code compression, qUnit unit testing, starting the server, and so on.

The above is all the content of the article "how to write maintainable JavaScript code". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to follow the industry information channel!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report