Nội dung bài viết
Video học lập trình mỗi ngày
Với 44 bài toán javascript được đưa vào trong một trang web "JavaScript Puzzlers!". Những câu hỏi này bao gồm một loạt các kiến thức trong javascript , bao gồm các nguyên mẫu JS, function, objects, closure, v.v. và chúng đều là những thứ rất chi tiết.
Trước tiên bạn muốn thử sức không? Hãy truy cập vào 44 bài toán tự kiểm tra năng lực javascript. Nếu bạn thực sự không hiểu or trả lời có tỷ lệ thấp dưới 19 câu thì hãy quay lại để xem phân tích của tôi.
Để giải thích cặn kẽ những điều này, tôi cũng tham khảo rất nhiều thông tin để bù đắp nhiều điểm mù trong kiến thức JS đến tận hôm nay. Đến nay dù trên dưới 10 năm lập trình js thì cũng chưa dám xưng tên vỗ ngực tự nói là "Đã học xong js". Cẩn thận những lời nói đó.
Và lý do giải thích quá nhiều trong một bài viết thì tôi chia ra 4 phần, và mỗi phần sẽ là 10 bài toán và kèm theo lời giải thích, riêng phần cuối sẽ là 14 câu. Hy vọng các bạn cũng thôi đi hết chặng đường này.
10 câu hỏi đầu tiên đánh giá thực lực javascript
//1 - map vs parseInt - Đáp án cuối cùng của bạn là gì? ["1", "2", "3"].map(parseInt) // A. ["1", "2", "3"] // B. [1, 2, 3] // C. [0, 1, 2] // D. other //2 - Null ?? [typeof null, null instanceof Object] // A. ["object", false] // B. [null, false] // C. ["object", true] // D. other 3 - Reduce ?? [ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ] // A. an error // B. [9, 0] // C. [9, NaN] // D. [9, undefined] 4 - Độ ưu tiên của toán tử var val = 'smtg'; console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing'); // A. Value is Something // B. Value is Nothing // C. NaN // D. other 5 - Thử sức với typeof var name = 'World!'; (function () { if (typeof name === 'undefined') { var name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })(); // A. Goodbye Jack // B. Hello Jack // C. Hello undefined // D. Hello World 6 - Bẫy của vòng lăpj var END = Math.pow(2, 53); var START = END - 100; var count = 0; for (var i = START; i <= END; i++) { count++; } console.log(count); // A. 0 // B. 100 // C. 101 // D. other 7 - Skill array with Filter var ary = [0,1,2]; ary[10] = 10; ary.filter(function(x) { return x === undefined; }); // A. [undefined x 7] // B. [0, 1, 2, 10] // C. [] // D. [undefined] 8 - Câu hỏi ưu tiên - IEEE 754 var two = 0.2; var one = 0.1; var eight = 0.8; var six = 0.6; [two - one == one, eight - six == two] // A. [true, false] // B. [false, false] // C. [true, false] // D. other 9 - Bẫy switch function showCase(value) { switch(value) { case 'A': console.log('Case A'); break; case 'B': console.log('Case B'); break; case undefined: console.log('undefined'); break; default: console.log('Do not know!'); } } showCase(new String('A')); // A. Case A // B. Case B // C. Do not know! // D. undefined 10 - Bẫy switch function showCase(value) { switch(value) { case 'A': console.log('Case A'); break; case 'B': console.log('Case B'); break; case undefined: console.log('undefined'); break; default: console.log('Do not know!'); } } showCase(String('A')); // A. Case A // B. Case B // C. Do not know! // D. undefined
Trên đây là 10 bài toán dầu tiên của phần này, nếu bạn trả lời trên 7 xem như bạn đạt, nếu không đạt hãy đọc tiếp để hiểu về phần phải thích.
Giải thích 10 bài toán javascript
Nếu các bạn chưa hài lòng với việc giải thích này, vui lòng chia sẻ quan điểm một cách lành manh, bỏi vì kiến thức rộng mở và bao la. Và ngay bản thân người viết cũng phải làm mới chính bản thân mình với những kiến thức phải tìm hiểu lại. Hy vọng chúng ta sẽ đi cùng nhau trong nghệ nghiệp của mình.
1 - parseInt và map
["1", "2", "3"].map(parseInt) // A. ["1", "2", "3"] // B. [1, 2, 3] // C. [0, 1, 2] // D. other
Đáp án trả lời là D.
Trên thực tế, kết quả trả về là [1, NaN, NaN] do hàm parseInt chỉ yêu cầu hai tham số parseInt(value, radix), trong khi hàm map có callbacks nhận ba tham số callback(currentValue, index, array).
Tài liệu MDN đã nói đến việc chỉ định trong parseInt() ở tham số thứ hai đó là radix là một giá trị số nguyên từ 2-36 (cơ số trong các hệ thống số toán học), để chuyển đổi cơ sở được chỉ định được sử dụng. Các mô tả dưới đây giải thích một cách chi tiết hơn những gì sẽ xảy ra khi radix không được cung cấp.
Quay lại với giải thích thì nếu tham số này bị bỏ qua hoặc giá trị của nó là 0, số sẽ được phân tích cú pháp trên cơ sở 10. Nếu tham số nhỏ hơn 2 hoặc lớn hơn 36, thì parseInt trả về NaN. Ngoài ra, lỗi chuyển đổi sẽ được trả lại NaN.
Bây giờ chúng ta hãy phân tích vấn đề.
parseInt("1", 0). Kết quả của được phân tích cú pháp dưới dạng số thập phân và trả về 1;
parseInt("2", 1) tham số thứ hai là không hợp lệ, trả về NaN;
parseInt("3", 2) trong hệ nhị phân, nó "3" là ký tự không hợp lệ và chuyển đổi không thành công, trả về NaN.
Những tài liệu tham khảo:
MDN: parseInt
Anonystick: Number.isNaN và isNaN
2 - Null và Object
[typeof null, null instanceof Object] // A. ["object", false] // B. [null, false] // C. ["object", true] // D. other
Đáp án trả lời là A.
Trong MDN và blog javascript cũng đề cập về null khá nhiều, thực tế cho đến bây giờ null cũng là một object cho nên typeof null có kết quả bằng object. Những đó là một lỗi của ECMAScript, đã đươc thừa nhận. Trên thực tế, là null là phải là null. Vì sao những nhà phát triển js thừa nhận type null sẽ bằng null nhưng vẫn phải thừa nhận là một object.
Quay lại lịch sử lâu đời đã tồn tại trong JavaScript gần hai thập kỷ và có thể không bao giờ được sửa vì liên quan đến quá nhiều hệ thống Web. Việc sửa nó sẽ tạo ra nhiều lỗi hơn và khiến nhiều hệ thống không thể hoạt động bình thường. Cho nên chấp nhận cho đến ngay hôm nay và có khi đến mai sau.
Cũng chính lý do đó cho nên instanceof được các nhà phát hành cho ra đời nhằm để kiểm tra xem một Object có một cấu trúc chuỗi chức năng nguyên mẫu của nó prototype sở hữu giá trị null. Và dùng để check lại vấn đề này, cho nên một cách dứt khoát để check là dùng instanceof.
Theo bản thân tôi thì hiểu về instanceof thì cẩn phải có một bài viết thật chi tiết hơn nữa. Vì nó liên quan đến quá trình lịch sử js.
Tài liệu tham khảo:
MDN: Null
MDN: instanceof
Anonystick: Tips and tricks
3 - Reduce javascript
Tiếp theo là một câu hỏi về reduce javascript, về hàm reduce thì rất rất nhiều bài nói về vấn đề này.
[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ] // A. an error // B. [9, 0] // C. [9, NaN] // D. [9, undefined]
Đáp án trả lời là A. Tài liệu MDN cho biết về Array.prototype.reduce() rất rõ ràng như sau:
"Nếu mảng rỗng, và initialValue không được cung cấp, gọi reduce() sẽ gây ra lỗi TypeError. Nếu mảng chỉ có một phần tử có giá trị (bất kể vị trí index) đồng thời không có initialValue, hoặc có initialValue nhưng mảng lại rỗng, thì giá trị duy nhất đó sẽ được trả về và callback sẽ không được gọi."
Những tài liệu tham khảo:
MDN: reduce
AST: 25 trường hợp sử dụng reduce javascript
AST: Reduce nâng cao
4 - Độ ưu tiên của toán tử
var val = 'smtg'; console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing'); // A. Value is Something // B. Value is Nothing // C. NaN // D. other
Câu trả lời là D.
Thực tế sẽ có kết quả là "Something", bởi vì trong js thì có một khía cạnh là độ ưu tiên của toán tử sử dụng trong mỗi câu lệnh, và ở đây là toán tử +.
Tài liệu tham khảo:
MDN: Toán tử ưu tiên trong javascript
5 - Tìm hiểu về Hoisting
var name = 'World!'; (function () { if (typeof name === 'undefined') { var name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })(); // A. Goodbye Jack // B. Hello Jack // C. Hello undefined // D. Hello World
Câu trả lời là A.
Để hiểu được về những bài toán này thì trước hết bạn phải hiểu về khái niệm Hoisting trong javascript. Để hiều hơn chúng ta có thể chuyển đổi hàm về như sau:
var name = 'World!'; (function () { var name; if (typeof name === 'undefined') { name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })();
Điều này có nghĩa là bạn có thể tham chiếu đến một hàm hoặc biến trước khi khai báo nó, hoặc bạn có thể nói: một biến hoặc hàm có thể được khai báo sau khi nó được tham chiếu.
Tài liệu nên tham khảo:
MDN: Hoisting
ANS: Hoisting trong javascript
6 - Bẫy sử dụng vòng lặp
var END = Math.pow(2, 53); var START = END - 100; var count = 0; for (var i = START; i <= END; i++) { count++; } console.log(count); // A. 0 // B. 100 // C. 101 // D. other
Câu trả lời là D.
Trong JavaScript, 2^53 là giá trị lớn nhất và không có giá trị nào lớn hơn. Do đó 2^53 + 1 == 2^53, chu kỳ này không thể được chấm dứt.
7 - Filter
var ary = [0,1,2]; ary[10] = 10; ary.filter(function(x) { return x === undefined; }); // A. [undefined x 7] // B. [0, 1, 2, 10] // C. [] // D. [undefined]
Câu trả lời là C.
Xem mô tả của tài liệu MDN chính thức: Filter gọi hàm callbacks một lần cho mỗi phần tử trong mảng và tạo một mảng mới với tất cả các phần tử thực hiện lệnh callbacks trả về true hoặc giá trị tương đương với true. callback sẽ chỉ được gọi trên các chỉ mục đã được gán và sẽ không được gọi trên các chỉ mục đã bị xóa hoặc không bao giờ được gán. Những phần tử không đạt yêu cầu kiểm tra gọi lại sẽ bị bỏ qua và sẽ không được đưa vào mảng mới.
Tài liệu nên đọc:
MDN: Filter
ANS: Filter nâng cao!
8 - IEEE 754 là gì?
var two = 0.2; var one = 0.1; var eight = 0.8; var six = 0.6; [two - one == one, eight - six == two] // A. [true, false] // B. [false, false] // C. [true, false] // D. other
Câu trả lời là C.
JavaScript sử dụng định dạng số dấu phẩy động có độ chính xác kép, là tiêu chuẩn IEEE 754 . Ở định dạng này, một số con số không thể được thể hiện, chẳng hạn như :, 0.1 + 0.2 = 0.30000000000000004 đây không phải là JavaScript, tất cả các ngôn ngữ áp dụng tiêu chuẩn này đều có vấn đề này, chẳng hạn như: Java, Python, v.v.
Tài liệu tham khảo:
Wiki: Double-precision floating-point format
9 - Một cái bẫy với switch
function showCase(value) { switch(value) { case 'A': console.log('Case A'); break; case 'B': console.log('Case B'); break; case undefined: console.log('undefined'); break; default: console.log('Do not know!'); } } showCase(new String('A')); // A. Case A // B. Case B // C. Do not know! // D. undefined
Câu trả lời là C.
Trong switch việc sử dụng toán tử === phán đoán bình đẳng nội bộ , và new String("A") trả về một đối tượng, và String("A") là một chuỗi trả về trực tiếp là "A".
Trong MDN có đề cập: Lưu ý rằng JavaScript phân biệt giữa các đối tượng Chuỗi và các giá trị chuỗi nguyên thủy. (Điều này cũng đúng với Boolean và Numbers.)
Tài liệu nên tham khảo:
MDN: Reference
10 - Bẫy tiếp của một trường hợp khác switch
function showCase(value) { switch(value) { case 'A': console.log('Case A'); break; case 'B': console.log('Case B'); break; case undefined: console.log('undefined'); break; default: console.log('Do not know!'); } } showCase(String('A')); // A. Case A // B. Case B // C. Do not know! // D. undefined
Câu trả lời rõ ràng là A.
Sự khác biệt duy nhất là ở trên không sử dụng từ khóa new, vì vậy trực tiếp trả về một chuỗi, trên thực tế, typeof string("A") === "string" kết quả là true. Để giải thích, vui lòng tham khảo giải thích của Câu số 9.
Bài biết được cập nhật mỗi lần câu hỏi được post lên page Tips Javascript