Cách viết JavaScript hiện đại: Phần 1: Tổng hợp các điểm mới có thể thực hành ngay

Nguồn: 旧石器時代のJavaScriptを書いてる各位に告ぐ、現代的なJavaScript超入門 Section1 ~すぐにでも現代っぽく出来るワンポイントまとめ~

Lời mở đầu

Trên mạng ngập tràn đủ loại thông tin và các hướng dẫn về JavaScript cũng rất nhiều, trong đó bao gồm nhiều thông tin đã cũ. Bài viết này tổng hợp các khác biệt giữa các cách viết Javascript cũ và mới.

Bài viết sử dụng các hướng dẫn cho đến ECMAScript 5và có giới thiệu một phần các chứ năng mới củaES6(ES2015), ES7.

Tác giả cố gắng viết một cách dễ hiểu nhất. Những ai muốn tìm hiểu sâu có thể tham khảo specs của ECMAScript hoặc MDN.

Các ví dụ trong bài sử dụng cho các browser tương ứng với ECMAScript 5 tức là không áp dụng cho IE8 trở xuống.

Mục lục

Phần 1: Tổng hợp các điểm mới có thể thực hành ngay

Phần 2: Làm quen với CommonJS module

Phần 3: Master Browserify

Phần 4: Tự động hóa xử lý dùng Gulp

Phần 5: Nhớ cú pháp của ES2015

Phần 1: Tổng hợp các điểm mới có thể thực hành ngay

1. Sử dụng HTML5

HTML được cải biến rất nhiều lần trong 20 năm nay. Các version HTML 4.01 và XHTML 1.0 là những phiên bản có nhiều bất cập đã tồn tại trong thời gian dài, thậm chí đến nay vẫn còn sót lại trên mạng. Năm 2014, HTML5 được công bố và được khuyến khích dùng cho các trang Web hiện nay.

Nhìn vào đoạn định nghĩa DOCTYPE HTML, ta có thể biết version nào đang được sử dụng.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!-- ↑Khả năng cao là HTML 4.01 -->

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- ↑Khả năng cao là XHTML 1.0 -->

<!DOCTYPE html>
<!-- ↑HTML5 -->

Cách viết đơn giản nhất là HTML5. Simple is BEST!

Bạn hãy rèn thói quen khi bắt đầu nhìn vào source code HTML, check xem có phải là đang dùng HTML5 không nhé.

2. Không dùng document.write

HTML5 không khuyến khích dùng document.write(hoặc writeln)

Các bạn có thể tham khảo ở đây

Lý do nên từ bỏ là bởi vì debug rất khó.

Hãy cố gắng dùng innerHTML thay thế

Code dùng document.write

<!DOCTYPE html>
<html lang="ja">
<head>
    <!-- Omit -->
</head>
<body>
    <h1>Show today's date</h1>
    <script>
        var date = new Date();
        var year = date.getYear();
        var month = date.getMonth()+1;
        var day = date.getDate();

        if(year < 1900) year += 1900;

        document.write("<p>Today is " + year + "/" + month + "/" + day + "</p>");
    </script>
</body>
</html>

Code cải tiến (hoạt động tương tự)

<!DOCTYPE html>
<html lang="ja">
<head>
    <!-- Omit -->
    <script>
        document.addEventListener("DOMContentLoaded",function(eve){
            var date = new Date();
            var year = date.getFullYear();
            var month = date.getMonth()+1;
            var day = date.getDate();

            document.body.innerHTML += "<p>Today is " + year + "/" + month + "/" + day + "</p>";
        },false);
    </script>
</head>
<body>
    <h1>Show today's date</h1>
</body>
</html>

Ởđây có dùng addEventListener, sẽ giải thích ở phần sau.

Sử dụng insertAdjacentHTML để tăng tốc

Sử dụng innerHTML sẽ cho tốc độ thấp.

insertAdjacentHTML sẽ cho tốc độ cao hơn.

Code hiện đại tốc độ nhanh

<!DOCTYPE html>
<html lang="ja">
<head>
    <!-- Omit -->
    <script>
        document.addEventListener("DOMContentLoaded",function(eve){
            var date = new Date();
            var year = date.getFullYear();
            var month = date.getMonth()+1;
            var day = date.getDate();

            document.body.insertAdjacentHTML("beforeend", "<p>Today is" + year + "/" + month + "/" + day + "</p>");
        },false);
    </script>
</head>
<body>
    <h1>Show today's date</h1>
</body>
</html>

Các bạn có thể tham khảo insertAdjacentHTML ở element.insertAdjacentHTML - MDN

3. Không sử dụng on○○ bừa bãi

Đặc biệt không dùng window.onload

Khi user làm một action nào đó thì hệ thống cần bắt được event đó bằng cách on○○ hoặc addEventListener("○○")

Vấn đề của on○○ là tối đa chỉ có thể đăng ký được một method.

Đăng ký event onload

// Alert này bị bỏ qua
window.onload = function(){
    alert("Finish load!");
};

// Chỉ có alert này được thực hiện
window.onload = function(){
    alert("It overrided the alert before");
};

Như thế có thể có người nghĩ là thường thì ghi đè cũng được nhưng để làm được thế thì chỉ có trường hợp một người viết code từ đầu đến cuối, hiểu được tất cả các event của code mới chắc chắn được.

EventTarget.addEventListener đã được sinh ra để giải quyết vấn đề này. Thay vì gán thì chuyển sang đăng ký hàm: Thực hiện các hàm theo thứ tự đăng ký.

//addEventListener
// Thực hiện đầu tiên
window.addEventListener("load",function(eve){
    alert("Finish load!");
},false);

// Thực hiện tiếp sau đó
window.addEventListener("load",function(eve){
    alert("After finishing load!");
},false);

attachEvent Các IE cũ không dùng addEventListener mà dùng attachEvent nhưng IE cũ đã gần như không còn nên hãy yên tâm dùng addEventListener.

Ngoài ra có thể dùng DOMContentLoaded để thay thế cho window.onload

<!DOCTYPE html>
<html lang="ja">
<head>
    <!-- Omit -->
    <script>
        // Đăng ký event bằng load
        window.addEventListener("load",function(){
            var $text = document.getElementById("text");
            alert("onload: <p>Tag content is " + $text.innerHTML);
        },false);

        // Đăng ký event bằng DOMContentLoaded
        document.addEventListener("DOMContentLoaded",function(eve){
            var $text = document.getElementById("text");
            alert("DOMContentLoaded: <p>Tag content is " + $text.innerHTML);
        },false);
    </script>
</head>
<body>
    <p id="text">Test</p>
    <div>
        <img src="test.png">
    </div>
</body>
</html>

load event được chạy sau khi HTML được load hoàn toàn còn DOMContentLoaded được chạy sau khi kết thúc phân tích cấu trúc của HTML tức là khi có thể dùng DOM là được chạy nên nhanh hơn nhiều.

Javascript sử dụng cho Web page hầu hết đáp ứng được yêu cầu sau khi phân tích DOM nên khuyến khích dùng DOMContentLoaded.

Tuy nhiên, DOMContentLoaded được gọi trước khi thu được chiều ngang của ảnh nên khi chỉnh sửa layout dùng độ rộng của ảnh thì không dùng.

Dùng load trong layout phù hợp với độ rộng ảnh

<!DOCTYPE html>
<html lang="ja">
<head>
    <!-- Omit -->
    <style>
        #imgContainer {
            background-color: black;
        }
    </style>
    <script>
        // Đăng ký event bằng load
        window.addEventListener("load",function(){
            var $imgContainer = document.getElementById("imgContainer");
            var $img = $imgContainer.querySelector("img");
            alert($img.width); // 863

            // Sử dụng độ rộng của ảnh xác định style
            $imgContainer.style.width = ($img.width + 20) + "px";
        },false);

        // Đăng ký event bằng DOMContentLoaded
        window.addEventListener("DOMContentLoaded",function(){
            var $imgContainer = document.getElementById("imgContainer");
            var $img = $imgContainer.querySelector("img");
            alert($img.width); // 0 ←DOMContentLoaded!!!!!
        },false);
    </script>
</head>
<body>
    <div id="imgContainer">
        <img src="image.png">
    </div>
</body>
</html>

4. Không dùng một phần method Date

Đặc biệt không nên dùng Date.prototype.getYear()

Nên dùng Date.prototype.getFullYear()

var date = new Date();
alert(date.getFullYear()); // 2016

5. Không trả về chuỗi ở biến thứ nhất của setTimeout và setInterval

//Code cũ:
setTimeout("hello()",1000);

function hello(){
    alert("Hi!");
}
//Code mới
setTimeout(hello,1000);

function hello(){
    alert("Hi!");
}

6. Không dùng for in bừa bãi

Object.prototype.gao = "がおがおがお~~~www";

var obj = {a:1, b:2};
for(var key in obj){
    alert(key); // "a"hoặc"b"đồng thời"gao"được liệt kê}
for in loop
Cách viết khác
Object.prototype.gao = "がおがおがお~~~www";

var obj = {a:1, b:2};
Object.keys(obj).forEach(function(key){
    alert(key); // Chỉ có "a" hoặc "b" được liệt kê
});

7. Không dùng eval

//Decode json
var text = JSON.stringify([
    {
        "name": "香風智乃",
        "age": 13,
    },
    {
        "name": "保登心愛",
        "age": 15,
    },
    {
        "name": "天々座理世",
        "age": 16,
    },
    {
        "name": "宇治松千夜",
        "age": 15,
    },
    {
        "name": "桐間紗路",
        "age": 15,
    },
]);

alert(typeof(text)); // "string"

// Cách viết này nguy hiểm
//var obj = eval(text);

// Dùng cách này
var obj = JSON.parse(text);

alert(typeof(obj)); // "object"
alert(obj[3].name); // "宇治松千夜"

8. Không dùng alert để debug

var obj = {name: "gao", age: 18};

// Khi dùng alert thì chỉ hiển thị [object Object]
alert(obj); // [object Object]

// Khi dùng console.log thì có thể check nội dung của object
console.log(obj); // Object {name: "gao", age: 18}

Nhưng cần chú ý không dùng console.log với IE8, 9 vì console chưa được định nghĩa.

8. Sử dụng strict mode

//Ví dụ sử dụng strict mode
(function(){
    "use strict";

    console.log(typeof(this)); // "undefined" ← not an "object"!

    try{
        arguments.callee;
    }
    catch(err){
        console.log("arguments.callee cannot use in StrictMode");
    }
})();