Tối giản mã nguồn JavaScript - một trong những nhiệm vụ quan trọng hàng đầu nếu bạn quan tâm đến hiệu suất của trang web. Hôm nay jsB@nk xin giới thiệu với bạn một bài viết cơ bản về vấn đề này.
Bài viết được viết bởi Nicholas C. Zakas - một bậc thầy về JavaScript hiện nay; thông qua bài viết này, bạn sẽ nắm được vài khái niệm cơ bản về vấn đề tối giản mã nguồn JavaScript như: lịch sử và nguồn gốc hình thành, ưu và khuyết điểm của kĩ thuật này cùng với vài mã nguồn đơn giản để bạn nắm rõ hơn.
- Demo
- Phóng to
- Tải lại
- Cửa sổ mới
Miễn phí web hosting 1 năm đầu tại iPage
Nếu bạn vẫn còn đang tìm kiếm một nhà cung cấp hosting đáng tin cậy, tại sao không dành chút thời gian để thử với iPage, chỉ với không quá 40.000 VNĐ/tháng, nhưng bạn sẽ được khuyến mãi kèm với quà tặng trị giá trên 10.000.0000 VNĐ nếu thanh toán cho 24 tháng ~ 900.000 VNĐ?
Có trên 1 triệu khách hàng hiện tại của iPage đã & đang hài lòng với dịch vụ, tuyệt đối chắc chắn bạn cũng sẽ hài lòng giống họ! Quan trọng hơn, khi đăng ký sử dụng web hosting tại iPage thông qua sự giới thiệu của chúng tôi, bạn sẽ được hoàn trả lại toàn bộ số tiền bạn đã sử dụng để mua web hosting tại iPage. Wow, thật tuyệt vời! Bạn không phải tốn bất kì chi phí nào mà vẫn có thể sử dụng miễn phí web hosting chất lượng cao tại iPage trong 12 tháng đầu tiên. Chỉ cần nói chúng tôi biết tài khoản của bạn sau khi đăng ký.
Nếu muốn tìm hiểu thêm về ưu / nhược điểm của iPage, bạn hãy đọc đánh giá của ChọnHostViệt.com nhé!
In the past few years, performance research has become more prevalent thanks to research by the Yahoo! Exceptional Performance Team and Google's Steve Souders. Most of that research studies not the individual HTML page, but the resources the page requires to display or behave correctly.
Although both CSS and JavaScript may be included within an HTML page, best practices encourage storing CSS and JavaScript in external files that can be downloaded and cached separately. Performance research asks: How can these external resources be downloaded and applied most efficiently? The first approach is to limit the number of external requests since the overhead of each HTTP request is high. The second approach? Make your code as small as possible.
The history of JavaScript byte savings
Douglas Crockford introduced JSMin in 2004 as a way to shrink JavaScript files before placing them into a production environment. His simple tool removed spaces, tabs, and comments from JavaScript files, effectively decreasing the size compared to the original source file. His rationale was sound: decreasing the size of JavaScript code results in faster downloads and a better user experience.
Three years later, Yahoo! engineer Julien Lecomte introduced the YUI Compressor. The YUI Compressor's goal was to shrink JavaScript files even further than JSMin by applying smart optimizations to the source code. In addition to removing comments, spaces, and tabs, the YUI Compressor also safely removes line breaks, further decreasing the overall file size. The biggest byte savings, though, come from replacing local variable names with one- or two-character names. For example, suppose you have the following function:
function sum(num1, num2) { return num1 + num2; }
YUI Compressor changes this to:
function sum(A,B){return A+B;}
Note that the two local variables, num1
and num2
, were replaced by A
and B
,
respectively. Since YUI Compressor actually parses the entire
JavaScript input, it can safely replace local variable names without
introducing code errors. The overall function continues to work as it
did originally since the variable names are irrelevant to the
functionality. On average, the YUI Compressor can compress files up to
18% more than JSMin.
These days, it's common to use a minification tool plus HTTP compression to further reduce JavaScript file size. This results in even greater savings than using either method alone.
Boosting minification
A couple of years ago, as I started debugging large amounts of production code, I realized that the YUI Compressor didn't apply variable replacement to a fairly significant portion of my code. Bothered by what I considered a lot of wasted bytes, I explored coding patterns to boost the YUI Compressor's minification powers. I presented my results, Extreme JavaScript Compression with YUI Compressor, internally at Yahoo!.
In my investigation, I discovered coding patterns that prevented YUI Compressor from performing variable name replacement. By modifying or avoiding these coding patterns, you can improve the YUI Compressor's performance.
JavaScript's evil features
Anyone who has followed Douglas Crockford's writing or lectures
knows about the "evil" parts of JavaScript: The parts that are
confusing and/or that prevent us from writing clean code that performs
well. The eval()
function and the with
statement are the two most egregious examples of evil JavaScript.
Though there are other considerations, both of these features force YUI
Compressor to stop replacing variables. To understand why, we need to
understand the intricacies of how each works.
Working with eval()
The eval()
statement's job is to take a string and interpret it as JavaScript code. For example:
eval("alert('Hello world!');");
The tricky part of eval()
is that it has access to all of the variables and functions that exist around it. Here's a more complex example:
var message = "Hello world!"; function doSomething() { eval("alert(message)"); }
When you call doSomething()
, an alert is displayed with the message, "Hello world!". That's because the string passed into eval()
accesses the global variable message
and displays it. Now consider what would happen if you automatically replaced the variable name message
:
var A = "Hello world!"; function doSomething() { eval("alert(message)"); }
Note that changing the variable name to A
results in an error when doSomething()
executes (since message
is undefined). YUI Compressor's first job is to preserve the functionality of your script, and so when it sees eval()
,
it stops replacing variables. This might not sound like such a bad idea
until you realize the full implications: Variable name replacement is
prevented not only in the local context where eval()
is called, but in all containing contexts as well. In the previous example, this means that both the context inside of doSomething()
and the global context cannot have variable names replaced.
Using eval()
anywhere in your code means that global variable names will never be changed. Consider the following example:
function handleJSONP(object) { return object; } function interpretJSONP(code) { var data = eval(code); //process data }
In this code, pretend that handleJSONP()
and interpretJSONP()
are defined in the midst of other functions. JSONP
is a widely used Ajax communication format that requires the response
to be interpreted by the JavaScript engine. For this example, a sample
JSONP response might look like this:
handleJSONP({message:"Hello world!"});
If you received this code back from the server via an XMLHttpRequest
call, the next step is to evaluate it, at which point eval()
becomes very useful. But just having eval()
in the code means that none of the global identifiers can have their
names replaced. The best option is to limit the number of global
variables you introduce.
You can often get away with this by creating a self-executing anonymous function, such as:
(function() { function handleJSONP(object) { return object; } function interpretJSONP(code) { var data = eval(code); //process data } })();
This code introduces no new global variables, but since eval()
is used, none of the variable names will be replaced. The actual result (110 bytes) is:
(Line wraps marked » -Ed.)
(function(){function handleJSONP(object){return object}function » interpretJSONP(code){var data=eval(code)}})();
The nice thing about JSONP is that it relies on the existence of
just one global identifier, the function to which the result must be
passed (in this case, handleJSONP()
). This means that it doesn't need access to any local variables or functions and gives you the opportunity to sequester the eval()
function in its own global function. Note that you also must move handleJSONP()
outside to be global as well so its name doesn't get replaced:
//my own eval function myEval(code) { return eval(code); } function handleJSONP(object) { return object; } (function() { function interpretJSONP(code) { var data = myEval(code); //process data } })();
The function myEval()
now acts like eval()
except that it cannot access local variables. It can, however, access
all global variables and functions. If the code being executed by eval()
will never need access to local variables, then this approach is the best. By keeping the only reference to eval()
outside of the anonymous function, you allow every variable name inside of that function to be replaced. Here's the output:
function myEval(code){return eval(code)}function handleJSONP » (a){return a}(function(){function a(b){var c=myEval(b)}})();
You can see that both interpretJSON()
, code
, and data
were replaced (with a
, b
, and c
, respectively). The result is 120 bytes, which you'll note is larger than the example without eval()
sequestered. That doesn't mean the approach is faulty, it's just that
this example code is far too small to see an impact. If you were to
apply this change on 100KB of JavaScript code, you would see that the
resulting code is much smaller than leaving eval()
in place.
Of course, the best option is not to use eval()
at all, as you'll avoid a lot of hoop-jumping to make the YUI Compressor happy. However, if you must, then sequestering the eval()
function is your best bet for optimal minification.
The with
statement
The with
statement is the second evil feature that
interferes with the YUI Compressor's variable replacement technique.
For those unfamiliar, the with
statement is designed (in
theory) to reduce the size of code by eliminating the need to write the
same variable names over and over again. Consider the following:
var object = { message: "Hello, ", messageSuffix: ", and welcome." }; object.message += "world" + object.messageSuffix; alert(object.message);
The with
statement allows you to rewrite this code as:
var object = { message: "Hello, ", messageSuffix: ", and welcome." }; with (object) { message += "world" + messageSuffix; alert(message); }
Effectively, the with
statement avoids the need to
repeat "object" multiple times within the code. But these savings come
at a cost. First, there are performance implications from using the with
statement, as local variables become slower to access. This happens because variables inside of a with
statement are ambiguous until execution time: They may be properties of the with
statement's context object or they may be variables from the function
or another execution context. To understand this ambiguity better, take
a look at the code when the local variable message
is added and the definition of object
is removed:
var message = "Yo, "; with (object) { message += "world" + messageSuffix; alert(message); }
When the identifier message
is used inside of the with
statement, it could be referencing the local variable message
or it could be referencing a property named message
on object
. Since JavaScript is a late binding language, there is no way to know the true reference for message
without completely executing the code and determining if object
has a property named message
. Witness how late binding affects this code:
function displayMessage(object) { var message = "Yo, "; with (object){ message += "world" + messageSuffix; alert(message); } } displayMessage({ message: "Hello, ", messageSuffix: ", and welcome." }); displayMessage({ messageSuffix: ", and welcome." });
The first time that displayMessage()
is called, the object passed in has a property named message
. When the with
statement executes, the reference to message
is mapped to the object property and so the displayed message is,
"Hello, world, and welcome." The second time, the object passed in has
only the messageSuffix
property, meaning the reference to message
inside of the with
statement refers to the local variable and the message displayed is therefore, "Yo, world, and welcome."
Since the YUI Compressor doesn't actually execute the JavaScript code, it has no way of knowing whether identifiers in a with
statement are object properties (in which case, it is not safe to replace them) or local variable references (in which case, it is safe to replace them). The YUI Compressor treats the with
statement the same as eval()
: when present, it will not perform variable replacement on the function or any containing execution contexts.
Unlike eval()
, there is no way to sequester the with
statement in such a way that it doesn't affect most of the code. The recommendation is to avoid using the with
statement at all. Even though it appears to save bytes at the time of
code writing, you actually end up losing bytes by forfeiting YUI
Compressor's variable replacement feature. The displayMessage()
function gets minified like this:
function displayMessage(object){var message="Yo, ";with(object) » {message+="world"+messageSuffix;alert(message)}};
This result is 112 bytes. If the function is rewritten to avoid the with
statement, displayMessage()
looks like this:
function displayMessage(object) { var message = "Yo, "; object.message += "world" + object.messageSuffix; alert(object.message); }
When minified, this new version of the function becomes:
function displayMessage(a){var b="Yo, ";a.message+="world"+ » a.messageSuffix;alert(a.message)};
The size of this result is 93 bytes, so even though the original source code is larger. The minified source code becomes smaller because we used variable replacement.
Conclusion
YUI Compressor's variable replacement functionality can give big
byte savings while minifying your JavaScript. Since the YUI Compressor
tries to avoid breaking your code by incorrectly replacing variable
names, it will turn off variable replacement when the eval()
function or with
statement is used. These "evil" features alter how JavaScript code is
interpreted and prevent the YUI Compressor from safely replacing
variable names, which costs you a large amount of byte savings. Avoid
this penalty by steering clear of eval()
or sequester it away from the rest of your code. Also, avoid the with
statement. These steps will ensure that your code doesn't get in the way of optimal minification.
- Lượt gửi (0)
- Mới
Tạo video doanh nghiệp của bạn bằng AI chỉ với giọng nói hoặc văn bản
chatGPTaz.com
Nói chuyện với ChatGPT bằng ngôn ngữ mẹ đẻ của bạn
Ứng dụng AI Video
Ứng dụng video AI MIỄN PHÍ đầu tiên của bạn
Deepfake Video
Deepfake AI Video Maker
Deepfake
Deepfake AI Video Maker
AI Deep Fake
Deepfake AI Video Maker
AIvidio
AI Video Mobile Solutions
AIvideos
AI Video Platform & Solutions
AIvedio
AI Video App Maker
Faceswap AI trực tuyến
Đổi mặt Video, Ảnh & GIF ngay lập tức với Công cụ AI mạnh mẽ - Faceswap AI Trực tuyến MIỄN PHÍ
Faceswap AI trực tuyến
Đổi mặt Video, Ảnh & GIF ngay lập tức với Công cụ AI mạnh mẽ - Faceswap AI Trực tuyến MIỄN PHÍ
Temu tặng $500 cho người dùng mới
Claim Free Temu $500 Credit via Affiliate & Influencer Program
Tín dụng quảng cáo TikTok miễn phí
Làm chủ quảng cáo TikTok cho hoạt động tiếp thị doanh nghiệp của bạn
Dall-E-OpenAI.com
Tự động tạo ra hình ảnh sáng tạo với AI
chatGPT4.win
Nói chuyện với ChatGPT bằng ngôn ngữ mẹ đẻ của bạn
Sản phẩm AI đầu tiên của Elon Musk - Grok/UN.com
Nói chuyện với Grok AI Chatbot bằng ngôn ngữ của bạn
Công cụ.win
Mở trung tâm công cụ miễn phí để mọi người sử dụng với hàng trăm công cụ
GateIO.gomymobi.com
Airdrop miễn phí để nhận, chia sẻ lên đến 150.000 đô la cho mỗi dự án
iPhoneKer.com
Tiết kiệm tới 630$ khi mua iPhone 16 mới
Mua Robot Tesla Optimus
Đặt mua Tesla Bot: Robot Optimus Gen 2 ngay hôm nay với giá dưới 20.000 đô la