Mục lục bài viết
Update: 2021-12-15 23:07:06,You Cần tương hỗ về JavaScript code review checklist. Quý quý khách trọn vẹn có thể lại Comment ở cuối bài để Tác giả được tương hỗ.
About The Author
Addy Osmani is an engineering manager working on Google Chrome. His team focuses on speed tools and frameworks, helping keep the web fast. Devoted to the More about Addy
Tóm lược đại ý quan trọng trong bài
E-Mail NewsletterYour (smashing) email
Weekly tips on front-end & UX.
Trusted by 190.000 folks.
Quick summary Before we start, Id like to pose a question: when was the last time you asked someone to review your code? Reviewing code is possibly the single best technique to improve the overall quality of your solutions, and if youre not actively taking advantage of it, then youre missing out on identifying bugs and hearing suggestions that could make your code better.
None of us write 100% bug-không lấy phí code all of the time, so dont feel theres a stigma attached to seeking help. Some of the most experienced developers in our industry, from framework authors to browser developers, regularly request reviews of their code from others; asking whether something could be tweaked should in no way be considered embarrassing. Reviews are a technique like any other and should be used where possible.
Today well look at where to get your code reviewed, how to structure your requests, and what reviewers look for. I was recently asked to review some code for a new JavaScript application, and thought Id like to share some of my feedback, because it covers some JavaScript fundamentals that are always useful to bear in mind.
More after jump! Continue reading below
Meet Touch Design for Mobile Interfaces, our brand-new Smashing Book on designing for mobile with proven, universal, human-centric guidelines. 400 pages, jam-packed with in-depth user research and guidelines you can apply immediately. Shipping starts early January 2022.
Jump to table of contents
Reviewing code goes hand in hand with maintaining strong coding standards. That said, standards dont usually prevent logical errors or misunderstandings about the quirks of a programming language, whether its JavaScript, Ruby, Objective-C or something else. Even the most experienced developers can make these kinds of mistakes, and reviewing code can greatly assist with catching them.
The first reaction most of us have to criticism is to defend ourselves (or our code), and perhaps lash back. While criticism can be slightly demoralizing, think of it as a learning experience that spurs us to do better and to improve ourselves; because in many cases, once weve calmed down, it actually does.
Also remember that no one is obliged to provide feedback on your work, and if the comments are indeed constructive, then be grateful for the time spent offering the input.
Reviews enable us to build on the experience of others and to benefit from a second pair of eyes. And at the end of the day, they are an opportunity for us to write better code. Whether we take advantage of them is entirely our choice.
Often the most challenging part is actually finding an experienced developer who you trust to do the review. Below are some places where you can request others to review your code (sometimes in other languages, too).
The following are some guidelines (based on experience) on how to structure your requests for code reviews, to increase the chances of them being accepted. You can be more liberal with them if the reviewer is on your team; but if the reviewer is external, then these might save you some time:
Jonathan Betz, a former developer at Google, once said that a code review should ideally address six things:
While I agree with this list, expanding it into an action guide of what reviewers should practically aim to give developers would be useful. So, reviewers should do the following:
Although a review by one developer can work well, an alternative approach is to bring more people into the process. This has a few distinct advantages, including reducing the load on individual reviewers and exposing more people to your implementation, which could potentially lead to more suggestions for improvements. It also allows a reviewers comments to be screened and corrected if they happen to make a mistake.
To assist the group, you may wish to employ a collaborative tool to allow all reviewers to simultaneously inspect and comment on your code. Luckily, a few decent ones out there are worth checking out:
(Image Source: joelogon)
On to the review.
A developer recently wrote in, asking me to review their code and provide some useful suggestions on how they might improve it. While Im certainly not an expert on reviewing code (dont let the above fool you), here are the problems and solutions that I proposed.
Problem: Functions and objects are passed as arguments to other functions without any type validation.
Feedback: Type validation is an essential step in ensuring that youre working only with input of a desired type. Without sanitization checks in place, you run the risk of users passing in just about anything (a string, a date, an array, etc.), which could easily break your application if you havent developed it defensively. For functions, you should do the following at a minimum:
if (callback && typeof callback === “function”)
/* rest of your logic */
else
/* not a valid function */
Unfortunately, a simple typeof check isnt enough on its own. As Angus Croll points out in his post Fixing the typeof operator, you need to be aware of a number of issues with typeof checking if youre using them for anything other than functions.
For example, typeof null returns object, which is technically incorrect. In fact, when typeof is applied to any object type that isnt a function, it returns object, not distinguishing between Array, Date, RegEx or whatever else.
The solution is to use Object.prototype.toString to call the underlying internal property of JavaScript objects known as [[Class]], the class property of the object. Unfortunately, specialized built-in objects generally overwrite Object.prototype.toString, but you can force the generic toString function on them:
Object.prototype.toString.call([1,2,3]); //”[object Array]”
You might also find Anguss function below useful as a more reliable alternative to typeof. Try calling betterTypeOf() against objects, arrays and other types to see what happens.
function betterTypeOf( input )
return Object.prototype.toString.call(input).match(/^[objects(.*)]$/)[1];
Here, parseInt() is being blindly used to parse an integer value of user input, but no base is specified. This can cause issues.
In JavaScript: The Good Parts, Douglas Crockford refers to parseInt() as being dangerous. Although you probably know that passing it a string argument returns an integer, you should also ideally specify a base or radix as the second argument, otherwise it might return unexpected output. Take the following example:
parseInt(’20’); // returns what you expect, however
parseInt(‘020’); // returns 16
parseInt(‘000020’); // returns 16
parseInt(‘020’, 10); // returns 20 as we’ve specified the base to use
Youd be surprised by how many developers omit the second argument, but it happens quite regularly. Remember that your users (if permitted to freely enter numeric input) wont necessarily follow standard number conventions (because theyre crazy!). Ive seen 020, ,20, ;20 and many other variations used, so do your best to parse as broad a range of inputs as possible. The following tricks to using parseInt() are occasionally better:
Math.floor(“020”); // returns 20
Math.floor(“0020”); //returns 20
Number(“020”); //returns 20
Number(“0020″); //returns 20
+”020”; //returns 20
Problem: Checks for browser-specific conditions being met are repeated throughout the code base (for example, feature detection, checks for supported ES5 features, etc.).
Feedback: Ideally, your code base should be as DRY as possible, and there are some elegant solutions to this problem. For example, you might benefit from the load-time configuration pattern here (also called load-time and init-time branching). The basic idea is that you test a condition only once (when the application loads) and then access the result of that test for all subsequent checks. This pattern is commonly found in JavaScript libraries that configure themselves at load time to be optimized for a particular browser.
This pattern could be implemented as follows:
var tools =
addMethod: null,
removeMethod: null
;
if(/* condition for native tư vấn */)
tools.addMethod = function(/* params */)
/* method logic */
else
/* fallback – eg. for IE */
tools.addMethod = function(/* */)
/* method logic */
The example below demonstrates how this can be used to normalize getting an XMLHttpRequest object.
var utils =
getXHR: null
;
if(window.XMLHttpRequest)
utils.getXHR = function()
return new XMLHttpRequest;
else if(window.ActiveXObject)
utils.getXHR = function()
/* this has been simplified for example sakes */
return new ActiveXObject(Microsoft.XMLHTTP);
For a great example, Stoyan Stefanov applies this to attaching and removing sự kiện listeners cross-browser, in his book JavaScript Patterns:
var utils =
addListener: null,
removeListener: null
;
// the implementation
if (typeof window.addEventListener === function)
utils.addListener = function ( el, type, fn )
el.addEventListener(type, fn, false);
;
utils.removeListener = function ( el, type, fn )
el.removeEventListener(type, fn, false);
;
else if (typeof document.attachEvent === function) // IE
utils.addListener = function ( el, type, fn )
el.attachEvent(on + type, fn);
;
utils.removeListener = function ( el, type, fn )
el.detachEvent(on + type, fn);
;
else // older browsers
utils.addListener = function ( el, type, fn )
el[on + type] = fn;
;
utils.removeListener = function ( el, type, fn )
el[on + type] = null;
;
Problem: The native Object.prototype is being extended regularly.
Feedback: Extending native types is generally frowned upon, and few (if any) popular code bases should dare to extend Object.prototype. The reality is that there is not likely a situation in which you absolutely need to extend it in this way. In addition to breaking the object-as-hash tables in JavaScript and increasing the chance of naming collisions, its generally considered bad practice, and modifying it should only be a last resort (this is quite different from extending your own custom object properties).
If for some reason you do end up extending the object prototype, ensure that the method doesnt already exist, and document it so that the rest of the team is aware why its necessary. You can use the following code sample as a guide:
if(typeof Object.prototype.myMethod != function)
Object.prototype.myMethod = function()
//implem
;
Juriy Zaytsev has a great post on extending native and host objects, which may be of interest.
Problem: Some of the code is heavily blocking the page because its either waiting on processes to complete or data to load before executing anything further.
Feedback: Page-blocking makes for a poor user experience, and there are a number of ways to work around it without impairing the application.
One solution is to use deferred execution (via promises and futures). The basic idea with promises is that, rather than issuing blocking calls for resources, you immediately return a promise for a future value that will eventually be fulfilled. This rather easily allows you to write non-blocking logic that can be run asynchronously. It is common to introduce callbacks into this equation that execute once the request completes.
Ive written a relatively comprehensive post on this with Julian Aubourg, if youre interested in doing this through jQuery, but it can of course be implemented with vanilla JavaScript as well.
Micro-framework Q. offers a CommonJS-compatible implementation of promises and futures that is relatively comprehensive and can be used as follows:
/* define a promise-only delay function that resolves when a timeout completes */
function delay(ms)
var deferred = Q..defer();
setTimeout(deferred.resolve, ms);
return deferred.promise;
/* usage of Q. with the ‘when’ pattern to execute a callback once delay fulfils the promise */
Q..when(delay(500), function ()
/* do stuff in the callback */
);
If youre looking for something more basic that can be read through, then here is Douglas Crockfords implementation of promises:
function make_promise()
var status = unresolved,
outcome,
waiting = [],
dreading = [];
function vouch( deed, func )
switch (status)
case unresolved:
(deed === fulfilled ? waiting : dreading).push(func);
break;
case deed:
func(outcome);
break;
;
function resolve( deed, value )
if (status !== unresolved)
throw new Error(The promise has already been resolved: + status);
status = deed;
outcome = value;
(deed == fulfilled ? waiting : dreading).forEach(function (func)
try
func(outcome);
catch (ignore)
);
waiting = null;
dreading = null;
;
return
when: function ( func )
vouch(fulfilled, func);
,
fail: function ( func )
vouch(smashed, func);
,
fulfill: function ( value )
resolve(fulfilled, value);
,
smash: function ( string )
resolve(smashed, string);
,
status: function ()
return status;
;
;
Problem: Youre testing for explicit numeric equality of a property using the == operator, but you should probably be using === instead
Feedback: As you may or may not know, the identity == operator in JavaScript is fairly liberal and considers values to be equal even if theyre of completely different types. This is due to the operator forcing a coercion of values into a single type (usually a number) prior to performing any comparison. The === operator will, however, not do this conversion, so if the two values being compared are not of the same type, then === will just return false.
The reason I recommend considering === for more specific type comparison (in this case) is that == is known to have a number of gotchas and is considered to be unreliable by many developers.
You might also be interested to know that in abstractions of the language, such as CoffeeScript, the == operator is completely dropped in favor of === beneath the hood due to the formers unreliability.
Rather than take my word for it, see the examples below of boolean checks for equality using ==, some of which result in rather unexpected outputs.
3 == “3” // true
3 == “03” // true
3 == “0003” // true
3 == “+3” //true
3 == [3] //true
3 == (true+2) //true
trn == 0 //true
“trn” == 0 //true
“t” == 0 // true
“tn” == 0 // true
“tr” == 0 // true
” ” == 0 // true
” t” == 0 // true
” ” == 0 // true
” rn ” == 0 //true
The reason that many of the (stranger) results in this list evaluate to true is because JavaScript is a weakly typed language: it applies type coercion wherever possible. If youre interested in learning more about why some of the expressions above evaluate to true, look at the Annotated ES5 guide, whose explanations are rather fascinating.
Back to the review. If youre 100% certain that the values being compared cannot be interfered with by the user, then proceed with using the == operator with caution. Just remember that === covers your bases better in the sự kiện of an unexpected input.
Problem: An uncached array length is being used in all for loops. This is particularly bad because youre using it when iterating through an HTMLCollection.
Heres an example:
for( var i=0; i<myArray.length;i++ )
/* do stuff */
Feedback: The problem with this approach (which I still see a number of developers using) is that the array length is unnecessarily re-accessed on each loops iteration. This can be very slow, especially when working with HTMLCollections (in which case, caching the length can be anywhere up to 190 times faster than repeatedly accessing it, as Nicholas C. Zakas mentions in his book High-Performance JavaScript). Below are some options for caching the array length.
/* cached outside loop */
var len = myArray.length;
for ( var i = 0; i < len; i++ )
/* cached inside loop */
for ( var i = 0, len = myArray.length; i < len; i++ )
/* cached outside loop using while */
var len = myArray.length;
while (len–)
A jsPerf test that compares the performance benefits of caching the array length inside and outside the loop, using prefix increments, counting down and more is also available, if you would like to study which performs the best.
Problem: jQuerys $.each() is being used to iterate over objects and arrays, in some cases while for is being used in others.
Feedback: In jQuery, we have two ways to seamlessly iterate over objects and arrays. The generic $.each iterates over both of these types, whereas $.fn.each() iterates over a jQuery object specifically (where standard objects can be wrapped with $() should you wish to use them with the latter). While the lower-level $.each performs better than $.fn.each(), both standard JavaScript for and while loops perform much better than either, as proven by this jsPerf test. Below are some examples of loop alternatives that also perform better:
/* jQuery $.each */
$.each(a, function()
e = $(this);
);
/* classic for loop */
var len = a.length;
for ( var i = 0; i < len; i++ )
//if this must be a jQuery object do..
e = $(a[i]);
//otherwise just e = a[i] should suffice
;
/* reverse for loop */
for ( var i = a.length; i– )
e = $(a[i]);
/* classic while loop */
var i = a.length;
while (i–)
e = $(a[i]);
/* alternative while loop */
var i = a.length – 1;
while ( e = a[i–] )
$(e)
;
You might find Angus Crolls post on Rethinking JavaScript for Loops an interesting extension to these suggestions.
Given that this is a data-centric application with a potentially large quantity of data in each object or array, you should consider a refactor to use one of these. From a scalability perspective, you want to shave off as many milliseconds as possible from process-heavy routines, because these can build up when hundreds or thousands of elements are on the page.
Problem: JSON strings are being built in-memory using string concatenation.
Feedback: This could be approached in more optimal ways. For example, why not use JSON.stringify(), a method that accepts a JavaScript object and returns its JSON equivalent. Objects can generally be as complex or as deeply nested as you wish, and this will almost certainly result in a simpler, shorter solution.
var myData = ;
myData.dataA = [a, b, c, d];
myData.dataB =
animal: cat,
color: brown
;
myData.dataC =
vehicles: [
type: ford,
tint: silver,
year: 2015
,
type: honda,
tint: black,
year: 2012
]
;
myData.dataD =
buildings: [
houses: [
streetName: sycamore close,
number: 252
,
streetName: slimdon close,
number: 101
]
]
;
console.log(myData); //object
var jsonData = JSON.stringify(myData);
console.log(jsonData);
/*
“dataA”:[“a”,”b”,”c”,”d”],”dataB”:”animal”:”cat”,”color”:”brown”,”dataC”:”vehicles”:[“type”:”ford”,”tint”:”silver”,”year”:”2015″,”type”:”honda”,”tint”:”black”,”year”:”2012″],”dataD”:”buildings”:[“houses”:[“streetName”:”sycamore close”,”number”:”252″,”streetName”:”slimdon close”,”number”:”101″]]
*/
As an extra debugging tip, if you would like to pretty-print JSON in your console for easier reading, then the following extra arguments to stringify() will achieve this:
JSON.stringify( foo: “hello”, bar: “world” , null, 4);
Problem: The namespacing pattern used is technically invalid.
Feedback: While namespacing is implemented correctly across the rest of the application, the initial check for namespace existence is invalid. Heres what you currently have:
if ( !MyNamespace )
MyNamespace = ;
The problem is that !MyNamespace will throw a ReferenceError, because the MyNamespace variable was never declared. A better pattern would take advantage of boolean conversion with an inner variable declaration, as follows:
if ( !MyNamespace )
var MyNamespace = ;
Problem: Some of the code is heavily blocking the page because its either waiting on processes to complete or data to load before executing anything further.
Feedback: Page-blocking makes for a poor user experience, and there are a number of ways to work around it without impairing the application.
One solution is to use deferred execution (via promises and futures). The basic idea with promises is that, rather than issuing blocking calls for resources, you immediately return a promise for a future value that will eventually be fulfilled. This rather easily allows you to write non-blocking logic that can be run asynchronously. It is common to introduce callbacks into this equation that execute once the request completes.
Ive written a relatively comprehensive post on this with Julian Aubourg, if youre interested in doing this through jQuery, but it can of course be implemented with vanilla JavaScript as well.
Micro-framework Q. offers a CommonJS-compatible implementation of promises and futures that is relatively comprehensive and can be used as follows:
/* define a promise-only delay function that resolves when a timeout completes */
function delay(ms)
var deferred = Q..defer();
setTimeout(deferred.resolve, ms);
return deferred.promise;
/* usage of Q. with the ‘when’ pattern to execute a callback once delay fulfils the promise */
Q..when(delay(500), function ()
/* do stuff in the callback */
);
If youre looking for something more basic that can be read through, then here is Douglas Crockfords implementation of promises:
function make_promise()
var status = unresolved,
outcome,
waiting = [],
dreading = [];
function vouch( deed, func )
switch (status)
case unresolved:
(deed === fulfilled ? waiting : dreading).push(func);
break;
case deed:
func(outcome);
break;
;
function resolve( deed, value )
if (status !== unresolved)
throw new Error(The promise has already been resolved: + status);
status = deed;
outcome = value;
(deed == fulfilled ? waiting : dreading).forEach(function (func)
try
func(outcome);
catch (ignore)
);
waiting = null;
dreading = null;
;
return
when: function ( func )
vouch(fulfilled, func);
,
fail: function ( func )
vouch(smashed, func);
,
fulfill: function ( value )
resolve(fulfilled, value);
,
smash: function ( string )
resolve(smashed, string);
,
status: function ()
return status;
;
;
Problem: Youre testing for explicit numeric equality of a property using the == operator, but you should probably be using === instead
Feedback: As you may or may not know, the identity == operator in JavaScript is fairly liberal and considers values to be equal even if theyre of completely different types. This is due to the operator forcing a coercion of values into a single type (usually a number) prior to performing any comparison. The === operator will, however, not do this conversion, so if the two values being compared are not of the same type, then === will just return false.
The reason I recommend considering === for more specific type comparison (in this case) is that == is known to have a number of gotchas and is considered to be unreliable by many developers.
You might also be interested to know that in abstractions of the language, such as CoffeeScript, the == operator is completely dropped in favor of === beneath the hood due to the formers unreliability.
Rather than take my word for it, see the examples below of boolean checks for equality using ==, some of which result in rather unexpected outputs.
3 == “3” // true
3 == “03” // true
3 == “0003” // true
3 == “+3” //true
3 == [3] //true
3 == (true+2) //true
trn == 0 //true
“trn” == 0 //true
“t” == 0 // true
“tn” == 0 // true
“tr” == 0 // true
” ” == 0 // true
” t” == 0 // true
” ” == 0 // true
” rn ” == 0 //true
The reason that many of the (stranger) results in this list evaluate to true is because JavaScript is a weakly typed language: it applies type coercion wherever possible. If youre interested in learning more about why some of the expressions above evaluate to true, look at the Annotated ES5 guide, whose explanations are rather fascinating.
Back to the review. If youre 100% certain that the values being compared cannot be interfered with by the user, then proceed with using the == operator with caution. Just remember that === covers your bases better in the sự kiện of an unexpected input.
Problem: An uncached array length is being used in all for loops. This is particularly bad because youre using it when iterating through an HTMLCollection.
Heres an example:
for( var i=0; i<myArray.length;i++ )
/* do stuff */
Feedback: The problem with this approach (which I still see a number of developers using) is that the array length is unnecessarily re-accessed on each loops iteration. This can be very slow, especially when working with HTMLCollections (in which case, caching the length can be anywhere up to 190 times faster than repeatedly accessing it, as Nicholas C. Zakas mentions in his book High-Performance JavaScript). Below are some options for caching the array length.
/* cached outside loop */
var len = myArray.length;
for ( var i = 0; i < len; i++ )
/* cached inside loop */
for ( var i = 0, len = myArray.length; i < len; i++ )
/* cached outside loop using while */
var len = myArray.length;
while (len–)
A jsPerf test that compares the performance benefits of caching the array length inside and outside the loop, using prefix increments, counting down and more is also available, if you would like to study which performs the best.
Problem: jQuerys $.each() is being used to iterate over objects and arrays, in some cases while for is being used in others.
Feedback: In jQuery, we have two ways to seamlessly iterate over objects and arrays. The generic $.each iterates over both of these types, whereas $.fn.each() iterates over a jQuery object specifically (where standard objects can be wrapped with $() should you wish to use them with the latter). While the lower-level $.each performs better than $.fn.each(), both standard JavaScript for and while loops perform much better than either, as proven by this jsPerf test. Below are some examples of loop alternatives that also perform better:
/* jQuery $.each */
$.each(a, function()
e = $(this);
);
/* classic for loop */
var len = a.length;
for ( var i = 0; i < len; i++ )
//if this must be a jQuery object do..
e = $(a[i]);
//otherwise just e = a[i] should suffice
;
/* reverse for loop */
for ( var i = a.length; i– )
e = $(a[i]);
/* classic while loop */
var i = a.length;
while (i–)
e = $(a[i]);
/* alternative while loop */
var i = a.length – 1;
while ( e = a[i–] )
$(e)
;
You might find Angus Crolls post on Rethinking JavaScript for Loops an interesting extension to these suggestions.
Given that this is a data-centric application with a potentially large quantity of data in each object or array, you should consider a refactor to use one of these. From a scalability perspective, you want to shave off as many milliseconds as possible from process-heavy routines, because these can build up when hundreds or thousands of elements are on the page.
Problem: JSON strings are being built in-memory using string concatenation.
Feedback: This could be approached in more optimal ways. For example, why not use JSON.stringify(), a method that accepts a JavaScript object and returns its JSON equivalent. Objects can generally be as complex or as deeply nested as you wish, and this will almost certainly result in a simpler, shorter solution.
var myData = ;
myData.dataA = [a, b, c, d];
myData.dataB =
animal: cat,
color: brown
;
myData.dataC =
vehicles: [
type: ford,
tint: silver,
year: 2015
,
type: honda,
tint: black,
year: 2012
]
;
myData.dataD =
buildings: [
houses: [
streetName: sycamore close,
number: 252
,
streetName: slimdon close,
number: 101
]
]
;
console.log(myData); //object
var jsonData = JSON.stringify(myData);
console.log(jsonData);
/*
“dataA”:[“a”,”b”,”c”,”d”],”dataB”:”animal”:”cat”,”color”:”brown”,”dataC”:”vehicles”:[“type”:”ford”,”tint”:”silver”,”year”:”2015″,”type”:”honda”,”tint”:”black”,”year”:”2012″],”dataD”:”buildings”:[“houses”:[“streetName”:”sycamore close”,”number”:”252″,”streetName”:”slimdon close”,”number”:”101″]]
*/
As an extra debugging tip, if you would like to pretty-print JSON in your console for easier reading, then the following extra arguments to stringify() will achieve this:
JSON.stringify( foo: “hello”, bar: “world” , null, 4);
Problem: The namespacing pattern used is technically invalid.
Feedback: While namespacing is implemented correctly across the rest of the application, the initial check for namespace existence is invalid. Heres what you currently have:
if ( !MyNamespace )
MyNamespace = ;
The problem is that !MyNamespace will throw a ReferenceError, because the MyNamespace variable was never declared. A better pattern would take advantage of boolean conversion with an inner variable declaration, as follows:
if ( !MyNamespace )
var MyNamespace = ;
//or
var myNamespace = myNamespace || ;
// Although a more efficient way of doing this is:
// myNamespace || ( myNamespace = );
// jsPerf test: jsperf/conditional-assignment
//or
if ( typeof MyNamespace == undefined )
var MyNamespace = ;
This could, of course, be done in numerous other ways. If youre interested in reading about more namespacing patterns (as well as some ideas on namespace extension), I recently wrote Essential JavaScript Namespacing Patterns. Juriy Zaytsev also has a pretty comprehensive post on namespacing patterns.
Thats it. Reviewing code is a great way to enforce and maintain quality, correctness and consistency in coding standards at as high a level as possible. I strongly recommend that all developers give them a try in their daily projects, because theyre an excellent learning tool for both the developer and the reviewer. Until next time, try getting your code reviewed, and good luck with the rest of your project!
(al, il)Explore more on
Smashing Newsletter
Tips on front-end & UX, delivered weekly in your inbox. Just the things you can actually use.
Front-End & UX Workshops, Online
With practical takeaways, live sessions, video recordings and a friendly Q.&A.
TypeScript in 50 Lessons
Everything TypeScript, with code walkthroughs and examples. And other printed books.
– Một số Keywords tìm kiếm nhiều : ” Review JavaScript code review checklist tiên tiến và phát triển nhất , Chia Sẻ Link Tải JavaScript code review checklist “.
You trọn vẹn có thể để lại Comments nếu gặp yếu tố chưa hiểu nghen.
#JavaScript #code #review #checklist