12/08/2018, 16:24

Javscript: Top 10 lỗi mà lập trình viên JavaScript thường mắc phải (Phần 2)

Index Javscript: Top 10 lỗi mà lập trình viên Javascript thường mắc phải (Phần 1) Javscript: Top 10 lỗi mà lập trình viên Javascript thường mắc phải (Phần 2) Sai lầm thứ 6: Incorrect use of function definitions inside for loops Cùng theo dõi đoạn code sau: var elements = ...

Index

  1. Javscript: Top 10 lỗi mà lập trình viên Javascript thường mắc phải (Phần 1)
  2. Javscript: Top 10 lỗi mà lập trình viên Javascript thường mắc phải (Phần 2)

Sai lầm thứ 6: Incorrect use of function definitions inside for loops

Cùng theo dõi đoạn code sau:

var elements = document.getElementsByTagName('input');
var n = elements.length;    // assume we have 10 elements for this example
for (var i = 0; i < n; i++) {
    elements[i].onclick = function() {
        console.log("This is element #" + i);
    };
}

Dựa theo code trên, nếu đầu vào có 10 element, nhấp vào bất kỳ một trong số chúng sẽ hiển thị "Đây là phần tử số 10"! Điều này là bởi vì, thời gian onclick được gọi cho bất kỳ phần tử nào, vòng lặp ở trên sẽ hoàn thành và giá trị của i đã là 10 (cho tất cả chúng). Đây là cách chúng ta có thể sửa các vấn đề code trên, tuy nhiên, để đạt được mong muốn:

var elements = document.getElementsByTagName('input');
var n = elements.length;    // assume we have 10 elements for this example
var makeHandler = function(num) {  // outer function
     return function() {   // inner function
         console.log("This is element #" + num);
     };
};
for (var i = 0; i < n; i++) {
    elements[i].onclick = makeHandler(i+1);
}

Trong phiên bản sửa đổi này, makeHandler được thực thi ngay lập tức mỗi khi chúng ta đi qua vòng lặp, mỗi lần sẽ nhận giá trị hiện tại của i + 1 và binding nó với một biến num scoped. Hàm bên ngoài trả về hàm bên trong (cũng sử dụng biến num) và onclick của phần tử được set thành hàm bên trong đó. Điều này đảm bảo rằng mỗi onclick sẽ nhận và sử dụng đúng giá trị i (thông qua biến num scoped).

Sai lầm thứ 7: Failure to properly leverage prototypal inheritance

Một điều đáng ngạc nhiên là một tỉ lệ cao các dev JavaScript không hiểu đầy đủ, và từ đó để tận dụng đầy đủ, các tính năng của thừa kế nguyên mẫu. Theo dõi đoạn code sau:

BaseObject = function(name) {
    if(typeof name !== "undefined") {
        this.name = name;
    } else {
        this.name = 'default'
    }
};

Có vẻ khá đơn giản. Nếu bạn cung cấp tên, hãy sử dụng nó, nếu không đặt tên thành'default'; ví dụ.:

var firstObj = new BaseObject();
var secondObj = new BaseObject('unique');

console.log(firstObj.name);  // -> Results in 'default'
console.log(secondObj.name); // -> Results in 'unique'

Nhưng nếu chúng ta làm thế này: delete secondObj.name;

Chúng ta sẽ có: console.log(secondObj.name); // -> Results in 'undefined'

Nhưng nó sẽ không được đẹp hơn cho điều này để trở lại 'default' ? Điều này có thể dễ dàng thực hiện, nếu chúng ta sửa đổi code ban đầu để thừa kế nguyên mẫu, như sau:

BaseObject = function (name) {
    if(typeof name !== "undefined") {
        this.name = name;
    }
};

BaseObject.prototype.name = 'default';

Với phiên bản này, BaseObject thừa hưởng thuộc tính name từ đối tượng nguyên mẫu của nó, nơi nó được đặt (theo mặc định) thành 'default'. Do đó, nếu constructor được gọi không có tên, tên sẽ mặc định là default. Và tương tự như vậy, nếu thuộc tính name bị xoá khỏi một instance của BaseObject, chuỗi nguyên mẫu sẽ được tìm kiếm và tên thuộc tính sẽ được lấy ra từ đối tượng nguyên mẫu, nơi giá trị của nó vẫn là 'default'. Vì vậy, bây giờ chúng ta nhận được:

var thirdObj = new BaseObject('unique');
console.log(thirdObj.name);  // -> Results in 'unique'

delete thirdObj.name;
console.log(thirdObj.name);  // -> Results in 'default'

Sai lầm thứ 8: Creating incorrect references to instance methods

Hãy define một object đơn giản, khởi tạo và instance của nó, như sau:

var MyObject = function() {}

MyObject.prototype.whoAmI = function() {
    console.log(this === window ? "window" : "MyObj");
};

var obj = new MyObject();

Bây giờ, để thuận tiện, hãy tạo một reference đến method whoAmI, có lẽ vì vậy chúng ta có thể truy cập nó bằng whoAmI () chứ không phải là obj.whoAmI (): var whoAmI = obj.whoAmI;

In giá trị mới của biến whoAmI: console.log(whoAmI);

Outputs:

function () {
    console.log(this === window ? "window" : "MyObj");
}

Trông có vẻ ổn. Nhưng bây giờ, hãy nhìn vào sự khác biệt khi chúng ta gọi function obj.whoAmI () so với whoAmI():

obj.whoAmI();  // outputs "MyObj" (as expected)
whoAmI();      // outputs "window" (uh-oh!)

Sai ở đâu ? Khi chúng ta thực hiện var whoAmI = obj.whoAmI;, biến mới whoAmI đã được định nghĩa trong global namespace. Kết quả là, giá trị của this là window, chứ không phải là obj instance của MyObject! Do đó, nếu chúng ta thực sự cần phải tạo ra một reference đến mộtmethod hiện có của một object, chúng ta cần đảm bảo thực hiện nó trong namespace của object đó, để bảo toàn giá trị của this. Một cách để thực hiện việc này là:

var MyObject = function() {}

MyObject.prototype.whoAmI = function() {
    console.log(this === window ? "window" : "MyObj");
};

var obj = new MyObject();
obj.w = obj.whoAmI;   // still in the obj namespace

obj.whoAmI();  // outputs "MyObj" (as expected)
obj.w();       // outputs "MyObj" (as expected)

Sai lầm thứ 9: Providing a string as the first argument to setTimeout or setInterval

Đối với người mới bắt đầu, cùng clear điều gì đó ở đây: Cung cấp một string như là đối số đầu tiên để setTimeout hoặc setInterval không phải là lỗi chính nó. Vấn đề ở đây là thêm hiệu suất. Những gì hiếm khi được giải thích là, nếu bạn pass một string như là đối số đầu tiên để setTimeout hoặc setInterval, nó sẽ được pass qua các function constructor để được chuyển đổi thành một function mới. Quá trình này có thể chậm và không hiệu quả, và hiếm khi cần thiết. Giải pháp thay thế là truyền vào một function. Chúng ta hãy xem một ví dụ. Ở đây, sau đó, sẽ là một sử dụng cách khá điển hình của setInterval và setTimeout, truyền một string như là tham số đầu tiên:

setInterval("logTime()", 1000);
setTimeout("logMessage('" + msgValue + "')", 1000);

Sự lựa chọn tốt hơn là pass trong một function như là đối số ban đầu; ví dụ.:

setInterval(logTime, 1000);   // passing the logTime function to setInterval

setTimeout(function() {       // passing an anonymous function to setTimeout
    logMessage(msgValue);     // (msgValue is still accessible in this scope)
  }, 1000);

Sai lầm thứ 10: Failure to use “strict mode”

Như đã giải thích trong JavaScript Hiring Guide, "strict mode" (tức là include 'use strict'; ở đầu source code của bạn) là một cách thực thi việc phân tích cú pháp và xử lý lỗi nghiêm ngặt hơn trên code JavaScript của bạn khi chạy, làm cho nó an toàn hơn.

Trong khi đó, không sử dụng strict mode thực chất không phải là "lỗi", nó ngày càng được khuyến khích sử dụng và sự thiếu sót của nó đang ngày càng trở thành hình thức xấu. Dưới đây là một số lợi ích chính của strict mode:

  • Makes debugging easier. Các lỗi nếu không được bỏ qua hoặc không thành công sẽ tạo ra lỗi hoặc throw exceptions, cảnh báo cho bạn sớm hơn các vấn đề trong code và hướng cho bạn nhanh hơn đến nguồn gốc của chúng.
  • Prevents accidental globals. Nếu không có strict mode, khi gán một giá trị cho một biến không khai báo, sẽ tự động tạo ra một biến global với tên đó. Đây là một trong những lỗi phổ biến nhất trong JavaScript. Trong strict mode, sẽ throw ra lỗi nếu cố làm vậy.
  • Eliminates this coercion. Nếu không có strict mode, một reference đến một giá trị this của null hoặc undefined sẽ tự động bị ràng buộc với global. Điều này có thể gây ra nhiều headfakes và pull-out-your-hair. Trong strict mode, reference một giá trị this của null hoặc undefined sẽ throws một lỗi.
  • Disallows duplicate property names or parameter values. Strict mode throws ra lỗi khi phát hiện một thuộc tính được đặt tên trùng lặp trong một object (ví dụ: var object = {foo: "bar", foo: "baz"};) hoặc một đối số được lập lại cho một function (ví dụ:function (foo val1, val2, val1) {}), do đó catch những gì gần như chắc chắn là một lỗi trong code của bạn mà bạn có thể, nếu không sẽ lãng phí rất nhiều thời gian tracking.
  • Makes eval() safer. Có một số khác biệt trong cách eval () hoạt động trong strict mode và ngoài strict mode. Đáng kể nhất, trong strict mode, các biến và các function khai báo bên trong một câu lệnh eval () không được tạo ra trong containing scope (chúng được tạo ra trong containing scope ở trong non-strict mode).
  • Throws error on invalid usage of delete. Toán tử delete (được sử dụng để loại bỏ thuộc tính từ các objects) không thể được sử dụng trên thuộc tính non-configurable của object. Non-strict code sẽ không âm thầm khi cố gắng delete một non-configurable property, trong khi đó, trong strict mode sẽ gây ra lỗi trong trường hợp đó.

Tóm lại:

Điều này cũng đúng với bất kỳ công nghệ nào, bạn càng hiểu rõ tại sao và như thế nào mà JavaScript hoạt động tốt và không hoạt động, thì code của bạn càng vững chắc và bạn sẽ có thể khai thác hiệu quả hơn sức mạnh thực sự của ngôn ngữ. Ngược lại, sự thiếu hiểu biết đúng về mô hình và khái niệm của JavaScript thì đây thực sự là nơi có nhiều vấn đề về. Làm quen với các sắc thái và sự tinh tế của ngôn ngữ là chiến lược hiệu quả nhất để nâng cao trình độ và nâng cao năng suất của bạn. Tránh nhiều lỗi JavaScript phổ biến sẽ giúp ích khi JavaScript của bạn không hoạt động.

Tham khảo

https://www.toptal.com/javascript/10-most-common-javascript-mistakes https://viblo.asia/p/tim-hieu-ve-strict-mode-trong-javascript-jaqG0QQevEKw

0