USING A CODE-CHECKING TOOL

Một phần của tài liệu reliable javascript how to code safely in the world s most dangerous language spencer richards 2015 07 13 Lập trình Java (Trang 135 - 142)

Code-checking tools perform static analysis, whereby they inspect the syntax and structure of source code without executing it. The purpose of the

inspection is to find and report likely incorrect language usage that may lead to errors when the code is executed. Some static analysis tools also report deviations from coding style rules, and useful metrics like computational complexity.

These types of tools are generally referred to as linters to reflect their relation to the C programming language static code analysis tool, lint, which was developed in the late 1970s.

Throughout this section, we’ll refer to static analysis tools as linters, and the process of performing static analysis as linting.

Making Your Code More Reliable with Linting Tools

Linting serves an important purpose when programming in JavaScript. If you’re a developer coming from a compiled language like C# or Java, you’re accustomed to the compiler informing you of grievous syntax errors such as omitting a statement-terminating semicolon or forgetting a closing curly brace. As an interpreted language, JavaScript doesn’t have a compiler that complains when you’ve made a mistake. You won’t be aware of any syntax errors until the code is executed.

Since you have adopted TDD, you will have a test to exercise every bit of

production code before you even write it, right? When practicing TDD, it may be enticing to skip the configuration and use of a linter because the unit tests guarantee that your code is functioning as it should.

Linters do not verify that code is correct. Your linter can’t tell you if the

function you’ve written will return the correct value. It can tell you, however, that you’ve written code within your function that has a questionable format and may cause it to return an incorrect value.

Suppose you’ve been tasked with building an airline reservation system. The customer has requested that you implement functionality that will determine whether a particular passenger is eligible for a complimentary upgrade to a first class seat.

In order to be upgraded, a passenger needs to have flown a certain number of

miles on the airline. For the purpose of this example, assume that a

passenger object contains the passenger’s first and last names, and an array integer trip lengths. Here’s what a passenger would look like when created with the object-literal creation pattern:

var testPassenger = { firstName: "Seth", lastName: "Richards", tripMileages: [

500, 600, 3400, 2500 ]

};

When calculating the miles a particular passenger has flown, you want to be able to reward members of the airline’s frequent flier program. To do so, you’ll provide a multiplier that will increase the mileage of each member’s flight to reward the passenger for his or her loyalty.

As the first step, you might create a function that will scale trip mileage by a multiplier. In test-driven fashion, Listing 2-40 shows unit tests that verify that the function behaves as expected.

LISTING 2-40: Unit tests for calculateUpgradeMilages (code filename: Linting\Linting_01_tests.js)

describe('calculateUpgradeMileages(tripMileages, memberMultiplier', function(){

var testPassenger = null;

beforeEach(function(){

testPassenger = { firstName : 'Seth', lastName : 'Richards', tripMileages : [

500, 600, 3400, 2500 ]

};

});

it('returns original mileages when multiplier is 1.0',

function(){

expect(calculateUpgradeMileages(testPassenger.tripMileages, 1.0))

.toEqual(testPassenger.tripMileages);

});

it('returns expected mileages when the memberMultiplier is 3.0', function(){

var expectedResults = [], multiplier = 3.0;

for(var i = 0; i<testPassenger.tripMileages.length; i++){

expectedResults[i] = testPassenger.tripMileages[i] * multiplier;

}

expect(calculateUpgradeMileages(testPassenger.tripMileages, multiplier))

.toEqual(expectedResults);

});

});

Listing 2-41 shows an implementation of calculateUpgradeMileages that causes the unit tests to pass.

LISTING 2-41: Implementation of calculateUpgradeMileages (code filename: Linting\Linting_01.js)

function calculateUpgradeMileages(tripMileages, memberMultiplier) {

var upgradeMileage = [], i = 0;

for (i = 0; i < tripMileages.length; i++) { var calcRewardsMiles = function(mileage) { return mileage * memberMultiplier;

};

upgradeMileage[i] = calcRewardsMiles(tripMileages[i]);

}

return upgradeMileage;

}

Finally, Figure 2.11 shows the Jasmine output of the unit tests.

Figure 2.11

As you can see, the function returns the expected result and the tests pass.

There is a potential problem in the code, however. Take a moment and see if you can identify the section of code in calculateUpgradeMileages that is troublesome. If it’s not immediately apparent to you, fear not because you have your linting tool to help.

Introducing JSHint

JSHint is an open-source static analysis tool that describes itself in the following way:

JSHint is a community-driven tool to detect errors and potential problems in JavaScript code and to enforce your team’s coding conventions.

http://www.jshint.com/about

JSHint is a fork of the JSLint project. JSLint was created and is maintained by Douglas Crockford. At the time they forked JSHint from JSLint, JSHint’s

creators felt that JSLint had become “too opinionated” and that a linting tool driven by the JavaScript community was needed.

Using JSHint

You can copy-and-paste your JavaScript code into the JSHint homepage at

http://www.jshint.com and the linter will execute automatically. While perhaps convenient for identifying a problem in a piece of sample code for a blog post (or book), using this mechanism to execute the linter is not suitable for development of a JavaScript solution with even limited complexity.

Because the calculateUpgradeMileages function is a piece of sample code for a book, you can use the online tool at http://www.jshint.com to find the questionable construct in the function.

Figure 2.12 shows what JSHint says about calculateUpgradeMileages.

Figure 2.12

As you can see, JSHint provides the following warning:

Don't make functions within a loop.

If you hadn’t noticed it before, JSHint is helpfully pointing out that you’ve declared a function within the loop that iterates over the list of mileages. Your unit tests pass, so in this case creation of a function inside a loop doesn’t

result in a logic error. So what’s the problem?

Remember that JSHint identifies questionable constructs in JavaScript code.

We can certainly agree that whether the result is logically correct or not, re- declaring a function during each iteration of a loop is questionable. At a minimum, it’s inefficient, and even though it didn’t in this example, it’s easy for the practice to result in incorrect and unreliable code.

NOTE See the JSHint documentation at

(http://www.jshint.com/docs/options/#loopfunc) for the “Don’t make functions in a loop” warning for an excellent example that leads to logically incorrect code.

Assume for a moment that you have a very good reason for placing your function within the loop. Each time you execute JSHint on your code, which should be very often, it will present that warning. Do you have to somehow make note that you (and the rest of the developers on your team) can safely ignore that particular warning for that particular line?

Thankfully, the answer is no. JSHint provides the ability to “relax” its rules globally via configuration, or locally via specially formatted comments.

Listing 2-42 shows the function again, this time with a comment that tells JSHint that you’re aware you’re creating a function inside a loop, and you’d like to relax the loopfunc rule:

LISTING 2-42: Implementation of calculateUpgradeMileages, which disables loopfunc checking (code filename:

Linting\Linting_02.js)

function calculateUpgradeMileages(tripMileages, memberMultiplier) {

var upgradeMileage = [], i = 0;

for (i = 0; i < tripMileages.length; i++) { /*jshint loopfunc: true */

var calcRewardsMiles = function(mileage) { return mileage * memberMultiplier;

};

/*jshint loopfunc: false */

upgradeMileage[i] = calcRewardsMiles(tripMileages[i]);

}

return upgradeMileage;

}

The comment /*jshint loopfunc: true */, which precedes creation of the function, tells JSHint to relax its loopfunc rule until it encounters another comment that tells it to stop relaxing the rule. The comment that causes the rule to no longer be relaxed, /*jshint loopfunc: false*/, appears right after the function declaration.

Providing the updated code that relaxes the loopfunc rule to the JSHint website yields the results shown in Figure 2.13.

Figure 2.13

As you can see, JSHint makes note that you’ve relaxed the loopfunc rule and no longer warns about creation of a function within a loop.

In this example, you’ve disabled a single rule for the minimum number of lines necessary. It is possible to tell JSHint not to process any rules for a section of code. We recommend that this not be done unless absolutely

necessary. If you’d suppressed the warning you were receiving by disabling all rules, it’s easy to imagine how a colleague maintaining or extending your code at some point in the future could be deprived of helpful linting warnings.

NOTE The JSHint website provides full documentation for its multitude of configuration options at http://www.jshint.com/docs/options.

If You Don’t Run It, Bugs Will Come

Ideally, JSHint should be executed automatically any time you’ve made changes to your JavaScript code. Any manual mechanism could result in forgetting to run, or purposely not running, the linting tool. Linting is not something to be left to the end of a project, or even to the next build (your project has a repeatable, automated build process, right?). Your linter should provide continuous feedback as you’re developing so that the code issues it identifies are addressed as early as possible.

JSHint is distributed in multiple ways, including an npm module for use with the server-side JavaScript engine node.js. There are also plugins to

automatically execute JSHint natively or via node.js on your JavaScript files

for many popular text editors and integrated development environments (IDEs), such as VIM, Emacs, Sublime Text, TextMate, and Visual Studio.

If your favorite editor or IDE doesn’t have a JSHint plugin available, node.js task-execution packages such as Grunt and Gulp may be configured to watch for changes in your JavaScript files and automatically execute JSHint when a change is detected.

Một phần của tài liệu reliable javascript how to code safely in the world s most dangerous language spencer richards 2015 07 13 Lập trình Java (Trang 135 - 142)

Tải bản đầy đủ (PDF)

(696 trang)