Những điều đặc biệt trong Javascript
Bài đăng này đã không được cập nhật trong 8 năm
Hôm nay tiếp tục loạt bài về javascript tôi xin trình bày những kiến thức cơ bản nhưng chưa hẳn ai cũng nắm rõ hết trong javascript. Javascript là một một ngôn ngữ lập trình kịch bản dựa vào đối tượng phát triển có sẵn hoặc tự định nghĩa ra. Nó có rất nhiều điểm tương đồng với các ngôn ngữ khác, ngoài ra còn có những đặc thù riêng sẽ được tôi trình bày ở 2 vấn đề chính sau đây
1. Tìm hiểu về các kiểu dữ liệu
a. Các kiểu dữ liệu trong javascript gồm 7 kiểu sau:
- null
- undefined
- boolean
- number
- string
- object
- symbol -- thêm vào trong ES6!
chú ý: Tất cả các kiểu dữ liệu trên ngoài object
được gọi là nguyên thủy
Toán tử typeof là để kiểm tra kiểu dữ liệu trong javascript và luôn luôn trả về một trong bảy giá trị string
ví dụ
typeof undefined === "undefined"; // true
typeof true === "boolean"; // true
typeof 42 === "number"; // true
typeof "42" === "string"; // true
typeof { life: 42 } === "object"; // true
// thêm vào trong ES6!
typeof Symbol() === "symbol"; // true
trên ví dụ kia bạn mới chỉ thấy 6 kiểu giá trị bởi vì có một trường hợp đặc biệt khi kết hợp với toán tử typeof
typeof null === "object"; // true
Nó sẽ đẹp và đúng hơn nếu nó trả về null
, nhưng nguồn gốc của lỗi này trong javascript đã kéo dài trong hai thập kỷ và có thể sẽ không bao giờ được sữa bởi vì đã có rất nhiều trang web cố sữa chữa lỗi này mà sinh ra rất nhiều lỗi, phá hủy chính các trang web này
null
cũng có giá trị nguyên thủy là falsy
nhưng lại trả về object khi kiểm tra bằng typeof
Do vậy, giá trị string thứ 7 khi kiểm tra bằng typeof vẫn có thể trả về là
typeof function a(){ /* .. */ } === "function"; // true
Nhiều người lầm tưởng function
là kiểu dữ liệu cấp cao được xây dựng trong javascript, đặc biệt trong thuộc tính của toán tử typeof. Tuy nhiên nếu bạn đọc spec, bạn sẽ hiểu rằng nó chỉ là subtype
của object.
Thực tế, function là những đối tượng rất hữu dụng. Nhưng quan trọng nhất, nó có những property
function a(b,c) {
/* .. */
}
a.length; //2
function object có length property được set bằng với số biến được khai báo. Trong trường hợp trên có 2 biến được khai báo là b và c nên length của function là 2
Còn mảng thì sao? Có nguồn gốc từ javascript, vậy nó có phải là kiểu dữ liệu đặc biệt không?
typeof [1,2,3] === "object"; // true
Không, nó chỉ là một object. Sẽ phù hợp nhất khi nghĩ mảng là kiểu dữ liệu phụ của đối tượng. Trong trường hợp thêm giá trị vào trong mảng thì mảng sẽ tự động cập nhật .length
property
b. Giá trị tương ứng với kiểu dữ liệu
Không giống như nhiều ngôn ngữ lập trình khác, khi khởi tạo biến thì gán một kiểu dữ liệu vào cho biến đấy. Ví dụ trong C, khởi tạo biến kiểu String thì không thể gán giá trị là integer cho biến đó được. Nhưng trong javascript, biến không có kiểu dự liệu cố định, mà giá trị sẽ quyết định kiểu dữ liệu của biến và các biến đều được khởi tạo bởi lệnh var + tên biến
. Các biến có thể giữ bất kỳ giá trị nào, bất cứ lúc nào.
Một cách nghĩ khác về kiểu dữ liệu của javascript là không có type enforcement
. Một biến có thể trong trường hợp này thì giữ giá trị string, trong trường hợp khác thì giữ giá trị là number.
Giá trị 42 có kiểu là number và kiểu dữ liệu của nó không thể thay đổi. Một giá trị khác, "42" là kiểu dữ liệu string và có thể tạo từ kiểu dữ liệu số 42 thông qua quá trình ép kiểu
var a = 42;
typeof a; // "number"
var a = true;
typeof a; // "boolean"
typeof typeof 42; // "string"
c. Khác nhau giữa undefined và "undeclared"
Biến không có giá trị hiện tại, thực sự không định nghĩa giá trị. Gọi typeof
để kiểm tra thì sẽ trả về undefined
var a;
typeof a; // "undefined"
var b = 42;
var c;
// later
b=c;
typeof b; // "undefined"
typeof c; // "undefined"
Việc này làm cho hầu hết các developer nghĩ rằng undifined
cũng đồng nghĩa với undeclared
. Tuy nhiên, trong javascript hai khái niệm này hoàn toàn khác nhau.
Một biến là undefined
thì đã được khai báo nhưng chưa được gán giá trị. Ngược lại một biến undeclared
thì chưa được khai báo. Ví dụ
var a;
a; // undefined
b; // ReferenceError: b is not defined
Một sự nhầm lẫn gây phiền nhiễu là các thông báo mà trình duyệt gán cho từng trường hợp. Như bạn thấy, thông báo là b is not defined
, rất dễ nhầm với b is undefined
. Tuy nhiên, undefined
và is not defined
là những điều rất khác nhau. Sẽ tốt hơn nếu các trình duyệt thông báo rõ ràng b is not found
và b is not found
, để giảm sự nhầm lẫn.
Có một thuộc tính đặc biệt gắn liền với typeof liên quan đến biến không được khai báo và tiếp tục gây nên sự nhầm lẫn. Cụ thể
var a;
typeof a; // "undefined"
typeof b; // "undefined"
Toán tử typeof trả về undefined
thậm chí với cả biến không được khai báo hoặc không được định nghĩa. Chú ý rằng không có lỗi đưa ra khi chúng ta chạy lệnh typeof b mặc dù b là biến vẫn chưa được khai báo. Đây là sự bảo về đặc biệt an toàn cho thuộc tính của typeof.
2. Tìm hiểu về cấu trúc khối lệnh trong javascript
Nếu ai đã từng học lập trình c và java thì sẽ thấy các kiểu cấu trúc khối lệnh trong javascript giống y hệt với hai ngôn ngữ trên. Ngoài ra trong javascript còn có một vài điểm khác biệt mà mình sẽ nêu sau đây:
- forEach
Dùng cho mảng(array) trong javascript, có cấu trúc là array.forEach(function(currentValue, index, arr), thisValue)
trong đó
currentValue: bắt buộc phải khai báo, giá trị của phần tử hiện tại
index: không bắt buộc phải khai báo. Vị trí trong mảng của phần tử hiện tại
arr: Không bắt buộc phải khai báo. Mảng đối tượng mà phần tử hiện tại thuộc về
A = [1,23,19,14,61]
A.forEach(function(currentValue, index, arr){
console.log("A["+index+"] is "+currentValue);
})
A[0] is 1
A[1] is 23
A[2] is 19
A[3] is 14
A[4] is 61
- for..in
Câu lệnh này được sử dụng để lặp tất cả các thuộc tính (properties) của một đối tượng. Tên biến có thể là một giá trị bất kỳ, chỉ cần thiết khi bạn sử dụng các thuộc tính trong vòng lặp. Ví dụ sau sẽ minh hoạ điều này
cú pháp for (<variable> in <object>) {//Các câu lệnh}
ví dụ
A = [1,3,2,6,7,4]
for (value in A)
{
console.log(A[value]);
}
// 1,3,2,6,7,4
- try..finally
Bạn có thể đã quen với cách làm việc của try..catch
. Nhưng bạn đã bao giờ suy nghĩ xem finally hoạt động như thế nào chưa, finally luôn chạy bất kỳ vấn đề gì xảy ra và luôn chạy sau try, catch kết thúc hoặc là chạy trước một vài lệnh khác. Cụ thể là lệnh return
trong khối lệnh try. Nó sẽ trả về một giá trị. Nhưng liệu nó sẽ trả về trước hoặc sau finally, câu trả lời là sau khi gọi finally.
function foo() {
try {
return 42;
}
finally {
console.log( "Hello" );
}
console.log( "never runs" );
}
console.log( foo() );
// Hello
// 42
Ngoài return trong try còn có thể chạy có lệnh sau: throw, try, catch
function example_throw() {
var x;
try {
if(x == "") throw "empty";
if(isNaN(x)) throw "not a number";
x = Number(x);
if(x < 5) throw "too low";
if(x > 10) throw "too high";
}
}
for (var i=0; i<10; i++) {
try {
continue;
}
finally {
console.log( i );
}
}
// 0 1 2 3 4 5 6 7 8 9
function example_break() {
bar: {
try {
return 42;
}
finally {
// break out of `bar` labeled block
break bar;
}
}
console.log( "Crazy" );
return "Hello";
}
console.log( foo() );
// Crazy
// Hello
3. Kết luận
Những điều kỳ diệu trong javascript còn rất nhiều nhưng bây giờ tôi chưa tìm hiểu hết được. Rất mong nhận được sự đóng góp ý kiến của các bạn để bài bài viết của tôi có thể hoàn chỉnh hơn. Bài viết này tôi có tham khảo ở các tài liệu sau
https://github.com/nghiabk/You-Dont-Know-JS/tree/master/types %26 grammar
All rights reserved