+1

Những điều đặc biệt trong Javascript

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, undefinedis 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 foundb 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí