4.1 Ngôn ngữ JavaScript bắt đầu nhanh

Tác giả:Tốt, Tạo: 2019-04-26 11:46:12, Cập nhật: 2019-04-27 11:53:43

Lịch sử

Phần này cung cấp một chút nền tảng về JavaScript để giúp bạn hiểu lý do tại sao nó như vậy.

JavaScript so với ECMAScript

ECMAScript là tên chính thức của JavaScript. Một cái tên mới trở nên cần thiết vì có một nhãn hiệu trên JavaScript (ban đầu được sở hữu bởi Sun, bây giờ bởi Oracle).

  • JavaScript có nghĩa là ngôn ngữ lập trình.
  • ECMAScript là tên được sử dụng bởi các thông số kỹ thuật ngôn ngữ. Do đó, bất cứ khi nào đề cập đến các phiên bản của ngôn ngữ, mọi người nói ECMAScript. Phiên bản hiện tại của JavaScript là ECMAScript 5; ECMAScript 6 hiện đang được phát triển.

Ảnh hưởng và bản chất của ngôn ngữ

Người tạo ra JavaScript, Brendan Eich, không có lựa chọn nào khác ngoài việc tạo ra ngôn ngữ rất nhanh (hoặc các công nghệ tồi tệ hơn khác sẽ được Netscape áp dụng). Ông mượn từ một số ngôn ngữ lập trình: Java (cụm ngữ, giá trị nguyên thủy so với đối tượng), Scheme và AWK (các hàm hạng nhất), Self (sự thừa kế nguyên mẫu) và Perl và Python (các chuỗi, mảng và biểu thức đều đặn).

JavaScript không có xử lý ngoại lệ cho đến ECMAScript 3, điều này giải thích tại sao ngôn ngữ thường tự động chuyển đổi giá trị và thường thất bại một cách im lặng: ban đầu nó không thể ném ngoại lệ.

Một mặt, JavaScript có những nét kỳ quặc và thiếu khá nhiều chức năng (biến số phạm vi khối, mô-đun, hỗ trợ phân loại phụ, v.v.). Mặt khác, nó có một số tính năng mạnh mẽ cho phép bạn làm việc xung quanh những vấn đề này. Trong các ngôn ngữ khác, bạn học các tính năng ngôn ngữ. Trong JavaScript, bạn thường học các mẫu thay vào đó.

Với những ảnh hưởng của nó, không có gì ngạc nhiên khi JavaScript cho phép một phong cách lập trình là một sự pha trộn của lập trình chức năng (các hàm bậc cao hơn; bản đồ tích hợp, giảm, v.v.) và lập trình hướng đối tượng (các đối tượng, thừa kế).

Định nghĩa

Phần này giải thích các nguyên tắc cú pháp cơ bản của JavaScript.

Một cái nhìn tổng quan về cú pháp

Một vài ví dụ về cú pháp:

// Two slashes start single-line comments

var x;  // declaring a variable

x = 3 + y;  // assigning a value to the variable `x`

foo(x, y);  // calling function `foo` with parameters `x` and `y`
obj.bar(3);  // calling method `bar` of object `obj`

// A conditional statement
if (x === 0) {  // Is `x` equal to zero?
    x = 123;
}

// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
    return a + b;
}

Lưu ý hai cách sử dụng khác nhau của dấu bằng:

  • Một dấu bằng (=) duy nhất được sử dụng để gán giá trị cho một biến.
  • Một dấu bằng ba (===) được sử dụng để so sánh hai giá trị (xem Các toán tử bình đẳng).

Những lời nói trái ngược với những biểu hiện

Để hiểu cú pháp của JavaScript, bạn nên biết rằng nó có hai loại cú pháp chính: các tuyên bố và biểu thức:

  • Một chương trình là một chuỗi các câu lệnh. Đây là một ví dụ về một câu lệnh, tuyên bố (tạo ra) một biến foo:
var foo;
  • Các biểu thức tạo ra các giá trị. Chúng là các đối số hàm, phía bên phải của một bài phân công, v.v.
3 * 7

Sự khác biệt giữa các câu lệnh và biểu thức được minh họa tốt nhất bởi thực tế là JavaScript có hai cách khác nhau để làm if-then-else như một câu lệnh:

var x;
if (y >= 0) {
    x = y;
} else {
    x = -y;
}

hoặc như một biểu thức:

var x = y >= 0 ? y : -y;

Bạn có thể sử dụng thứ hai như một đối số hàm (nhưng không phải là thứ nhất):

myFunction(y >= 0 ? y : -y)

Cuối cùng, bất cứ nơi nào JavaScript mong đợi một câu lệnh, bạn cũng có thể sử dụng một biểu thức; ví dụ:

foo(7, 1);

Toàn bộ đường thẳng là một câu lệnh (được gọi là câu lệnh biểu thức), nhưng cuộc gọi hàm foo ((7, 1) là một biểu thức.

Dấu chấm

Các dấu chấm là tùy chọn trong JavaScript. Tuy nhiên, tôi khuyên bạn nên luôn bao gồm chúng, bởi vì nếu không JavaScript có thể đoán sai về kết thúc của một câu lệnh. Các chi tiết được giải thích trong Automatic Semicolon Insertion.

Các dấu chấm chấm chấm kết thúc các câu lệnh, nhưng không phải là các khối. Có một trường hợp bạn sẽ thấy dấu chấm chấm sau một khối: một biểu thức hàm là một biểu thức kết thúc bằng một khối. Nếu một biểu thức như vậy xuất hiện cuối cùng trong một câu lệnh, nó được theo sau bởi dấu chấm chấm:

// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { };  // function expr. inside var decl.

Các ý kiến

JavaScript có hai loại bình luận: bình luận một dòng và bình luận nhiều dòng.

x++; // single-line comment

Các bình luận nhiều dòng được giới hạn bởi /* và */:

/* This is
   a multiline
   comment.
 */

Các biến và giao nhiệm vụ

Các biến trong JavaScript được tuyên bố trước khi chúng được sử dụng:

var foo;  // declare variable `foo`

Nhiệm vụ

Bạn có thể tuyên bố một biến và gán một giá trị cùng một lúc:

var foo = 6;

Bạn cũng có thể gán giá trị cho một biến hiện có:

foo = 4;  // change variable `foo`

Các nhà điều hành giao nhiệm hợp chất

Có các toán tử phân bổ hợp chất như +=. Hai phân bổ sau đây là tương đương:

x += 1;
x = x + 1;

Các định danh và tên biến

Trình nhận dạng là những tên đóng các vai trò cú pháp khác nhau trong JavaScript. Ví dụ, tên của một biến là một bộ nhận dạng.

Tương tự, ký tự đầu tiên của một mã nhận dạng có thể là bất kỳ chữ cái Unicode nào, ký hiệu đô la ($), hoặc dấu gạch dưới (_). Các ký tự tiếp theo có thể là bất kỳ chữ số Unicode nào.

arg0
_tmp
$elem
π

Các nhận dạng sau đây là những từ được dành riêng, chúng là một phần của cú pháp và không thể được sử dụng như tên biến (bao gồm tên hàm và tên tham số):

img

Ba nhận dạng sau đây không phải là các từ được dành riêng, nhưng bạn nên đối xử với chúng như thể chúng là:

img

Cuối cùng, bạn cũng nên tránh xa tên của các biến toàn cầu tiêu chuẩn. Bạn có thể sử dụng chúng cho các biến địa phương mà không phá vỡ bất cứ điều gì, nhưng mã của bạn vẫn trở nên khó hiểu.

Giá trị

JavaScript có nhiều giá trị mà chúng ta mong đợi từ các ngôn ngữ lập trình: boolean, số, chuỗi, mảng, v.v. Tất cả các giá trị trong JavaScript đều có thuộc tính. Mỗi thuộc tính có một khóa (hoặc tên) và một giá trị. Bạn có thể nghĩ về thuộc tính như các trường của một bản ghi. Bạn sử dụng toán tử chấm (.) để đọc một thuộc tính:

value.propKey

Ví dụ, chuỗi abc có độ dài thuộc tính:

> var str = 'abc';
> str.length
3

Điều trước đây cũng có thể được viết như sau:

> 'abc'.length
3
The dot operator is also used to assign a value to a property:

> var obj = {};  // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123

Và bạn có thể sử dụng nó để gọi các phương thức:

> 'hello'.toUpperCase()
'HELLO'

Trong ví dụ trước đây, chúng tôi đã gọi phương thức toUpperCase() trên giá trị hello.

Các giá trị nguyên thủy so với các vật thể

JavaScript làm cho một sự phân biệt khá tùy ý giữa các giá trị:

  • Các giá trị nguyên thủy là boolean, số, chuỗi, null và undefined.
  • Tất cả các giá trị khác là các đối tượng. Một sự khác biệt lớn giữa hai là cách chúng được so sánh; mỗi đối tượng có một bản sắc độc đáo và chỉ (chính xác) bằng với chính nó:
> var obj1 = {};  // an empty object
> var obj2 = {};  // another empty object
> obj1 === obj2
false
> obj1 === obj1
true

Ngược lại, tất cả các giá trị nguyên thủy mã hóa cùng một giá trị được coi là giống nhau:

> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true

Hai phần tiếp theo giải thích các giá trị và đối tượng nguyên thủy chi tiết hơn.

Các giá trị nguyên thủy

Sau đây là tất cả các giá trị nguyên thủy (hoặc nguyên thủy ngắn gọn):

  • Boolean: đúng, sai (xem Boolean)
  • Số: 1736, 1.351 (xem Số)
  • Dây: abc, abc (xem Dây)
  • Hai nonvalues: undefined, null (xem undefined và null)

Những người nguyên thủy có những đặc điểm sau:

So sánh theo giá trị

nội dung được so sánh:

> 3 === 3
true
> 'abc' === 'abc'
true

### Luôn không thay đổi Các thuộc tính không thể thay đổi, thêm hoặc xóa:

> var str = 'abc';

> str.length = 1; // try to change property `length`
> str.length      // ⇒ no effect
3

> str.foo = 3; // try to create property `foo`
> str.foo      // ⇒ no effect, unknown property
undefined

(Đọc một thuộc tính không xác định luôn trả về không xác định.)

Các đối tượng

Tất cả các giá trị không nguyên thủy là các đối tượng.

  • Các đối tượng đơn giản, có thể được tạo bởi các đối tượng chữ (xem các đối tượng đơn):
{
    firstName: 'Jane',
    lastName: 'Doe'
}

Đối tượng trước có hai thuộc tính: giá trị của thuộc tính firstName là Jane và giá trị của thuộc tính lastName là Doe.

  • Các mảng, có thể được tạo bởi các chữ cái mảng (xem Mảng):
[ 'apple', 'banana', 'cherry' ]

Mảng trước có ba phần tử có thể truy cập thông qua các chỉ số số. Ví dụ, chỉ số của apple là 0.

  • Các biểu thức đều đặn, có thể được tạo ra bởi các chữ cái biểu thức đều đặn (xem Expressions Regular):
/^a+b+$/

Các đối tượng có các đặc điểm sau:

So sánh theo tham chiếu

Các danh tính được so sánh; mỗi giá trị có danh tính của riêng nó:

> ({} === {})  // two different empty objects
false

> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true

Đổi đổi theo mặc định

Bạn thường có thể tự do thay đổi, thêm và loại bỏ các thuộc tính (xem các đối tượng duy nhất):

> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123

không xác định và không có giá trị

Hầu hết các ngôn ngữ lập trình đều có giá trị biểu thị thông tin bị thiếu. JavaScript có hai loại nonvalues, undefined và null:

  • undefined có nghĩa là không có giá trị.
> var foo;
> foo
undefined

Các tham số bị thiếu không được xác định:

> function f(x) { return x }
> f()
undefined

Nếu bạn đọc một thuộc tính không tồn tại, bạn sẽ không xác định:

> var obj = {}; // empty object
> obj.foo
undefined
  • null có nghĩa là không có đối tượng. Nó được sử dụng như một không giá trị bất cứ khi nào một đối tượng được mong đợi (các tham số, cuối cùng trong một chuỗi các đối tượng, v.v.).

Cảnh báo

undefined và null không có thuộc tính, thậm chí cả các phương thức tiêu chuẩn như toString().

Kiểm tra không xác định hoặc không

Các hàm thường cho phép bạn chỉ ra một giá trị bị thiếu thông qua undefined hoặc null. Bạn có thể làm tương tự thông qua kiểm tra rõ ràng:

if (x === undefined || x === null) {
    ...
}

Bạn cũng có thể khai thác thực tế rằng cả undefined và null đều được coi là sai:

if (!x) {
    ...
}

Cảnh báo

false, 0, NaN, và cũng được coi là sai (xem Truthy và Falsy).

Phân loại các giá trị bằng cách sử dụng typeof và instanceof

Có hai toán tử để phân loại các giá trị: typeof chủ yếu được sử dụng cho các giá trị nguyên thủy, trong khi instanceof được sử dụng cho các đối tượng. kiểu như thế này:

typeof value

Nó trả về một chuỗi mô tả type của giá trị.

> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'

Bảng sau liệt kê tất cả các kết quả của loại:

img

typeof null trả về object là một lỗi không thể sửa chữa, bởi vì nó sẽ phá vỡ mã hiện có.

ví dụ của trông như thế này:

value instanceof Constr

Nó trả về true nếu value là một đối tượng đã được tạo bởi constructor Constr (xem Constructors: Factories for Objects).

> var b = new Bar();  // object created by constructor Bar
> b instanceof Bar
true

> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object  // Array is a subconstructor of Object
true

> undefined instanceof Object
false
> null instanceof Object
false

Boolean

Loại boolean nguyên thủy bao gồm các giá trị true và false. Các toán tử sau đây tạo ra boolean:

  • Các toán tử logic nhị phân: && (And),ཁྱོད་རྣམས་ལ་ (Or)
  • Định tố toán tử logic:! (Không)
  • Các toán tử so sánh: Các toán tử bình đẳng: ===,!==, ==,!=
  • Các toán tử sắp xếp (đối với chuỗi và số): >, >=, <, <=

Sự thật và sự giả dối

Bất cứ khi nào JavaScript mong đợi một giá trị boolean (ví dụ, cho điều kiện của câu lệnh if), bất kỳ giá trị nào cũng có thể được sử dụng. Nó sẽ được giải thích là true hoặc false. Các giá trị sau đây được giải thích là false:

  • không xác định, không
  • Boolean: sai
  • Số lượng: 0, NaN
  • Dòng chữ:

Tất cả các giá trị khác (bao gồm tất cả các đối tượng!) được coi là đúng. Các giá trị được giải thích là sai được gọi là sai, và các giá trị được giải thích là đúng được gọi là đúng. Boolean ((), được gọi là một hàm, chuyển đổi tham số của nó thành boolean. Bạn có thể sử dụng nó để kiểm tra cách giải thích một giá trị:

> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true

Các toán tử logic nhị phân

Các toán tử logic nhị phân trong JavaScript là mạch ngắn. nghĩa là, nếu toán tử đầu tiên đủ để xác định kết quả, thì toán tử thứ hai không được đánh giá. Ví dụ, trong các biểu thức sau, hàm foo() không bao giờ được gọi:

false && foo()
true  || foo()

Hơn nữa, các toán tử logic nhị phân trả về một trong các toán tử của chúng, có thể hoặc không phải là boolean.

Và (&&)

Nếu operand đầu tiên là false, trả lại nó. Nếu không, trả lại operand thứ hai:

> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'

Hoặc (kamu)

Nếu operand đầu tiên là true, trả về nó. Nếu không, trả về operand thứ hai:

> 'abc' || 123
'abc'
> '' || 123
123

Các nhà hoạt động bình đẳng

JavaScript có hai loại bình đẳng:

  • Bình thường, hoặc nghiêm tốn, (không bằng nhau): == và!=
  • Cụ thể (không bằng nhau): === và!==

Bình đẳng bình thường coi (quá) nhiều giá trị là bằng nhau (các chi tiết được giải thích trong Bình đẳng bình thường (==,!=)), có thể che giấu lỗi.

Số lượng

Tất cả các số trong JavaScript là dấu phẩy động:

> 1 === 1.0
true

Các số đặc biệt bao gồm:

NaN (không phải là một con số) Giá trị lỗi:

> Number('xyz')  // 'xyz' can’t be converted to a number
NaN

Vô hạn Cũng chủ yếu là một giá trị lỗi:

> 3 / 0
Infinity
> Math.pow(2, 1024)  // number too large
Infinity

Infinity lớn hơn bất kỳ số nào khác (ngoại trừ NaN). Tương tự, -Infinity nhỏ hơn bất kỳ số nào khác (ngoại trừ NaN). Điều đó làm cho các số này hữu ích như các giá trị mặc định (ví dụ, khi bạn đang tìm kiếm mức tối thiểu hoặc tối đa).

Các nhà khai thác

JavaScript có các toán tử số học sau đây (xem Các toán tử số học):

  • Thêm: số1 + số2
  • Trừ: số 1 - số 2
  • Xấp: số1 * số2
  • Phân khúc: số 1 / số 2
  • Phần còn lại: số 1 % số 2
  • Tăng: ++ biến, biến++
  • Decrement: biến, biến
  • - giá trị
  • Chuyển đổi thành số: + giá trị

Đối tượng toàn cầu Math (xem Math) cung cấp nhiều hoạt động số học hơn, thông qua các hàm.

JavaScript cũng có các toán tử cho các hoạt động bitwise (ví dụ, bitwise And; xem Bitwise Operators).

Các dây

Các chuỗi có thể được tạo trực tiếp thông qua các chữ cái chuỗi. Những chữ cái đó được giới hạn bằng dấu ngoặc kép đơn hoặc đôi. Backslash () thoát khỏi các ký tự và tạo ra một vài ký tự điều khiển. Dưới đây là một số ví dụ:

'abc'
"abc"

'Did she say "Hello"?'
"Did she say \"Hello\"?"

'That\'s nice!'
"That's nice!"

'Line 1\nLine 2'  // newline
'Backlash: \\'

Các ký tự đơn được truy cập thông qua ngoặc kép:

> var str = 'abc';
> str[1]
'b'

Độ dài thuộc tính đếm số ký tự trong chuỗi:

> 'abc'.length
3

Giống như tất cả các nguyên thủy, các chuỗi không thể thay đổi; bạn cần tạo một chuỗi mới nếu bạn muốn thay đổi một chuỗi hiện có.

Các toán tử chuỗi

Các chuỗi được liên kết thông qua toán tử cộng (+), chuyển đổi các toán tử khác thành một chuỗi nếu một trong những toán tử là một chuỗi:

> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'

Để liên kết chuỗi trong nhiều bước, sử dụng toán tử +=:

> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'

Phương pháp chuỗi

Dây chuỗi có nhiều phương thức hữu ích (xem phương thức nguyên mẫu chuỗi).

> 'abc'.slice(1)  // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'

> '\t xyz  '.trim()  // trim whitespace
'xyz'

> 'mjölnir'.toUpperCase()
'MJÖLNIR'

> 'abc'.indexOf('b')  // find a string
1
> 'abc'.indexOf('x')
-1

Tuyên bố

Các điều kiện và vòng lặp trong JavaScript được giới thiệu trong các phần sau.

Điều kiện

Tuyên bố if có một điều khoản then và một điều khoản else tùy chọn được thực hiện tùy thuộc vào một điều kiện boolean:

if (myvar === 0) {
    // then
}

if (myvar === 0) {
    // then
} else {
    // else
}

if (myvar === 0) {
    // then
} else if (myvar === 1) {
    // else-if
} else if (myvar === 2) {
    // else-if
} else {
    // else
}

Tôi khuyên bạn nên luôn luôn sử dụng dấu ngoặc (họ biểu thị các khối không hoặc nhiều câu lệnh). Nhưng bạn không cần phải làm như vậy nếu một điều khoản chỉ là một câu lệnh duy nhất (tương tự như vậy đối với các câu lệnh dòng kiểm soát cho và trong khi):

if (x < 0) return -x;

Sau đây là lệnh chuyển đổi. Giá trị của fruit quyết định trường hợp nào được thực thi:

switch (fruit) {
    case 'banana':
        // ...
        break;
    case 'apple':
        // ...
        break;
    default:  // all other cases
        // ...
}

operand sau trường hợp có thể là bất kỳ biểu thức nào; nó được so sánh thông qua === với tham số chuyển đổi.

Vòng lặp

Chuỗi for có định dạng sau:

for (⟦«init»⟧; ⟦«condition»⟧; ⟦«post_iteration»⟧)
    «statement»

init được thực hiện ở đầu vòng lặp. condition được kiểm tra trước mỗi lần lặp lặp vòng lặp; nếu nó trở thành false, thì vòng lặp được kết thúc. post_iteration được thực hiện sau mỗi lần lặp vòng lặp.

Ví dụ này in tất cả các yếu tố của mảng arr trên bảng điều khiển:

for (var i=0; i < arr.length; i++) {
    console.log(arr[i]);
}

Vòng lặp while tiếp tục lặp trên cơ thể của nó trong khi điều kiện của nó giữ:

// Same as for loop above:
var i = 0;
while (i < arr.length) {
    console.log(arr[i]);
    i++;
}

Vòng lặp do-while tiếp tục lặp trên cơ thể của nó trong khi điều kiện của nó được giữ.

do {
    // ...
} while (condition);

Trong tất cả các vòng lặp:

  • Bỏ ra vòng lặp.
  • tiếp tục bắt đầu lặp lại vòng lặp mới.

Chức năng

Một cách để xác định một hàm là thông qua một tuyên bố hàm:

function add(param1, param2) {
    return param1 + param2;
}

Mã trước đó định nghĩa một hàm, cộng, có hai tham số, param1 và param2, và trả về tổng của cả hai tham số.

> add(6, 1)
7
> add('a', 'b')
'ab'

Một cách khác để xác định add() là gán một biểu thức hàm cho một biến add:

var add = function (param1, param2) {
    return param1 + param2;
};

Một biểu thức hàm tạo ra một giá trị và do đó có thể được sử dụng để truyền trực tiếp các hàm như các đối số cho các hàm khác:

someOtherFunction(function (p1, p2) { ... });

Tuyên bố chức năng được treo lên

Tuyên bố chức năng được hoistedmoved in their entirety to the beginning of the current scope. Điều đó cho phép bạn tham khảo các hàm được tuyên bố sau:

function foo() {
    bar();  // OK, bar is hoisted
    function bar() {
        ...
    }
}

Lưu ý rằng trong khi các tuyên bố var cũng được nâng lên (xem Các biến được nâng lên), các nhiệm vụ được thực hiện bởi chúng không phải là:

function foo() {
    bar();  // Not OK, bar is still undefined
    var bar = function () {
        // ...
    };
}

Các lập luận về biến đặc biệt

Bạn có thể gọi bất kỳ hàm nào trong JavaScript với một số lượng tùy ý của các đối số; ngôn ngữ sẽ không bao giờ phàn nàn. Tuy nhiên, nó sẽ làm cho tất cả các tham số có sẵn thông qua các đối số biến đặc biệt. các đối số trông giống như một mảng, nhưng không có bất kỳ phương thức mảng nào:

> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0]  // read element at index 0
'a'

Quá nhiều hay quá ít tranh luận

Hãy sử dụng hàm sau đây để khám phá cách xử lý quá nhiều hoặc quá ít tham số trong JavaScript (công thức toArray( được hiển thị trong Chuyển đổi các đối số thành một Array):

function f(x, y) {
    console.log(x, y);
    return toArray(arguments);
}

Các thông số bổ sung sẽ bị bỏ qua (ngoại trừ đối số):

> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]

Các tham số bị thiếu sẽ làm cho giá trị không xác định:

> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]

Các thông số tùy chọn

Sau đây là một mô hình phổ biến để gán các giá trị mặc định cho các tham số:

function pair(x, y) {
    x = x || 0;  // (1)
    y = y || 0;
    return [ x, y ];
}

Trong dòng (1), toán tử x trả về x nếu nó là truthy (không là null, undefined, v.v.).

> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]

Đặt ra một điều kiện

Nếu bạn muốn thực thi một arity (một số tham số cụ thể), bạn có thể kiểm tra arguments.length:

function pair(x, y) {
    if (arguments.length !== 2) {
        throw new Error('Need exactly 2 arguments');
    }
    ...
}

Chuyển đổi các đối số thành một mảng

arguments không phải là một mảng, nó chỉ giống như một mảng (xem Array-Like Objects and Generic Methods). Nó có chiều dài thuộc tính, và bạn có thể truy cập các phần tử của nó thông qua các chỉ mục trong ngoặc kép. Tuy nhiên, bạn không thể xóa các phần tử hoặc gọi bất kỳ phương thức mảng nào trên nó. Do đó, đôi khi bạn cần chuyển đổi các đối số thành một mảng, đó là những gì hàm sau làm (nó được giải thích trong Array-Like Objects and Generic Methods):

function toArray(arrayLikeObject) {
    return Array.prototype.slice.call(arrayLikeObject);
}

Xử lý ngoại lệ

Cách phổ biến nhất để xử lý ngoại lệ (xem Chương 14) là như sau:

function getPerson(id) {
    if (id < 0) {
        throw new Error('ID must not be negative: '+id);
    }
    return { id: id }; // normally: retrieved from database
}

function getPersons(ids) {
    var result = [];
    ids.forEach(function (id) {
        try {
            var person = getPerson(id);
            result.push(person);
        } catch (exception) {
            console.log(exception);
        }
    });
    return result;
}

Điều try bao quanh mã quan trọng, và điều catch được thực hiện nếu một ngoại lệ được ném vào bên trong điều try.

> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]

Chế độ nghiêm ngặt

Chế độ nghiêm ngặt (xem Chế độ nghiêm ngặt) cho phép nhiều cảnh báo hơn và làm cho JavaScript trở thành một ngôn ngữ sạch hơn (chế độ không nghiêm ngặt đôi khi được gọi là sloppy mode).

'use strict';

Bạn cũng có thể bật chế độ nghiêm ngặt cho mỗi chức năng:

function functionInStrictMode() {
    'use strict';
}

Phạm vi phạm vi biến và đóng cửa

Trong JavaScript, bạn tuyên bố các biến thông qua var trước khi sử dụng chúng:

> var x;
> x
undefined
> y
ReferenceError: y is not defined

Bạn có thể tuyên bố và khởi tạo một số biến với một câu lệnh var duy nhất:

var x = 1, y = 2, z = 3;

Nhưng tôi khuyên bạn nên sử dụng một câu lệnh cho mỗi biến (lý do được giải thích trong Syntax).

var x = 1;
var y = 2;
var z = 3;

Bởi vì nâng (xem Các biến được nâng), thường tốt nhất là tuyên bố các biến ở đầu của một hàm.

Các biến có phạm vi chức năng

Phạm vi của một biến luôn là hàm hoàn chỉnh (trái ngược với khối hiện tại). Ví dụ:

function foo() {
    var x = -512;
    if (x < 0) {  // (1)
        var tmp = -x;
        ...
    }
    console.log(tmp);  // 512
}

Chúng ta có thể thấy rằng biến tmp không bị giới hạn trong khối bắt đầu trong dòng (1); nó tồn tại cho đến khi kết thúc hàm.

Các biến được nâng lên

Mỗi tuyên bố biến được nâng lên: tuyên bố được di chuyển đến đầu của hàm, nhưng các bài tập mà nó thực hiện vẫn được đặt.

function foo() {
    console.log(tmp); // undefined
    if (false) {
        var tmp = 3;  // (1)
    }
}

Bên trong, hàm trước được thực hiện như thế này:

function foo() {
    var tmp;  // hoisted declaration
    console.log(tmp);
    if (false) {
        tmp = 3;  // assignment stays put
    }
}

Việc đóng cửa

Mỗi hàm vẫn kết nối với các biến của các hàm xung quanh nó, ngay cả sau khi nó rời khỏi phạm vi mà nó được tạo ra. Ví dụ:

function createIncrementor(start) {
    return function () {  // (1)
        start++;
        return start;
    }
}

Chức năng bắt đầu ở dòng (1) rời khỏi ngữ cảnh mà trong đó nó được tạo nhưng vẫn được kết nối với phiên bản bắt đầu trực tiếp:

> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8

Một đóng là một hàm cộng với kết nối với các biến của phạm vi xung quanh.

Mô hình IIFE: Đưa ra một phạm vi mới

Đôi khi bạn muốn giới thiệu một phạm vi biến mới, ví dụ, để ngăn biến trở thành toàn cầu. Trong JavaScript, bạn không thể sử dụng một khối để làm như vậy; bạn phải sử dụng một hàm. Nhưng có một mẫu để sử dụng một hàm theo cách giống như khối. Nó được gọi là IIFE (lời biểu thức hàm được gọi ngay lập tức, phát âm là iffy):

(function () {  // open IIFE
    var tmp = ...;  // not a global variable
}());  // close IIFE

Hãy chắc chắn nhập ví dụ trước đó chính xác như được hiển thị (ngoại trừ các bình luận). Một IIFE là một biểu thức hàm được gọi ngay sau khi bạn xác định nó. Bên trong hàm, một phạm vi mới tồn tại, ngăn chặn tmp trở thành toàn cầu. Hãy tham khảo giới thiệu phạm vi mới thông qua một IIFE để biết chi tiết về IIFE.

Trường hợp sử dụng IIFE: chia sẻ vô tình thông qua đóng cửa

Các kết thúc giữ kết nối của họ với các biến bên ngoài, mà đôi khi không phải là những gì bạn muốn:

var result = [];
for (var i=0; i < 5; i++) {
    result.push(function () { return i });  // (1)
}
console.log(result[1]()); // 5 (not 1)
console.log(result[3]()); // 5 (not 3)

Giá trị được trả về trong dòng (1) luôn luôn là giá trị hiện tại của i, không phải giá trị mà nó có khi hàm được tạo ra. Sau khi vòng lặp được hoàn thành, i có giá trị 5, đó là lý do tại sao tất cả các hàm trong mảng trả về giá trị đó. Nếu bạn muốn hàm trong dòng (1) nhận được ảnh chụp tức thời của giá trị hiện tại của i, bạn có thể sử dụng IIFE:

for (var i=0; i < 5; i++) {
    (function () {
        var i2 = i; // copy current i
        result.push(function () { return i2 });
    }());
}

Các vật thể và các nhà xây dựng

Phần này bao gồm hai cơ chế định hướng đối tượng cơ bản của JavaScript: đối tượng đơn và cấu trúc (là các nhà máy cho các đối tượng, tương tự như các lớp trong các ngôn ngữ khác).

Mục tiêu duy nhất

Như tất cả các giá trị, các đối tượng có thuộc tính. Bạn có thể, trên thực tế, xem xét một đối tượng là một tập hợp các thuộc tính, trong đó mỗi thuộc tính là một cặp (khóa, giá trị).

Trong JavaScript, bạn có thể trực tiếp tạo các đối tượng đơn giản, thông qua các chữ cái đối tượng:

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

Đối tượng trước có tên thuộc tính và mô tả. Bạn có thể đọc (get) và viết (set) thuộc tính:

> jane.name  // get
'Jane'
> jane.name = 'John';  // set
> jane.newProperty = 'abc';  // property created automatically

Các thuộc tính có giá trị hàm như describe được gọi là phương thức.

> jane.describe()  // call method
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'

Các trong toán tử kiểm tra xem một thuộc tính tồn tại:

> 'newProperty' in jane
true
> 'foo' in jane
false

Nếu bạn đọc một thuộc tính không tồn tại, bạn sẽ nhận được giá trị undefined.

> jane.newProperty !== undefined
true
> jane.foo !== undefined
false

Động thái xóa loại bỏ một thuộc tính:

> delete jane.newProperty
true
> 'newProperty' in jane
false

Chìa khóa sở hữu tùy ý

Chìa khóa thuộc tính có thể là bất kỳ chuỗi nào. Cho đến nay, chúng ta đã thấy các khóa thuộc tính trong các chữ cái đối tượng và sau trình diễn chấm. Tuy nhiên, bạn chỉ có thể sử dụng chúng theo cách đó nếu chúng là các nhận dạng (xem nhận dạng và tên biến). Nếu bạn muốn sử dụng các chuỗi khác như các khóa, bạn phải trích dẫn chúng trong một chữ cái đối tượng và sử dụng ngoặc kép để lấy và đặt thuộc tính:

> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;

Các ngoặc kép cũng cho phép bạn tính toán khóa của một thuộc tính:

> var obj = { hello: 'world' };
> var x = 'hello';

> obj[x]
'world'
> obj['hel'+'lo']
'world'

Phương pháp chiết xuất

Nếu bạn trích xuất một phương thức, nó sẽ mất kết nối với đối tượng.

Như một ví dụ, hãy quay lại đối tượng Jane trước đây:

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

Chúng ta muốn trích xuất phương thức describe từ jane, đưa nó vào một biến func, và gọi nó. Tuy nhiên, điều đó không hoạt động:

> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined

Giải pháp là sử dụng phương thức bind() mà tất cả các hàm đều có. Nó tạo ra một hàm mới mà nó luôn có giá trị được đưa ra:

> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'

Các chức năng bên trong một phương pháp

Mỗi hàm có biến đặc biệt của riêng nó this. Điều này là không thuận tiện nếu bạn tổ hợp một hàm bên trong một phương thức, bởi vì bạn không thể truy cập vào phương thức s this từ hàm.

var jane = {
    name: 'Jane',
    friends: [ 'Tarzan', 'Cheeta' ],
    logHiToFriends: function () {
        'use strict';
        this.friends.forEach(function (friend) {
            // `this` is undefined here
            console.log(this.name+' says hi to '+friend);
        });
    }
}

Gọi logHiToFriends tạo ra lỗi:

> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined

Chúng ta hãy xem xét hai cách để khắc phục điều này.

logHiToFriends: function () {
    'use strict';
    var that = this;
    this.friends.forEach(function (friend) {
        console.log(that.name+' says hi to '+friend);
    });
}

Hoặc, forEach có một tham số thứ hai cho phép bạn cung cấp một giá trị cho điều này:

logHiToFriends: function () {
    'use strict';
    this.friends.forEach(function (friend) {
        console.log(this.name+' says hi to '+friend);
    }, this);
}

Các biểu thức hàm thường được sử dụng như là các đối số trong các cuộc gọi hàm trong JavaScript.

Nhà sản xuất: Nhà máy sản xuất đồ vật

Cho đến nay, bạn có thể nghĩ rằng các đối tượng JavaScript chỉ là bản đồ từ chuỗi đến giá trị, một khái niệm được gợi ý bởi các chữ cái đối tượng JavaScript, trông giống như các chữ cái bản đồ / từ điển của các ngôn ngữ khác. Tuy nhiên, các đối tượng JavaScript cũng hỗ trợ một tính năng thực sự định hướng đối tượng: thừa kế. Phần này không giải thích đầy đủ cách thức thừa kế JavaScript hoạt động, nhưng nó cho bạn thấy một mô hình đơn giản để bắt đầu. Xem Chương 17 nếu bạn muốn biết thêm.

Ngoài các hàm và phương thức real, các hàm đóng một vai trò khác trong JavaScript: chúng trở thành các constructorfactory cho các đối tượng nếu được gọi thông qua toán tử mới.

// Set up instance data
function Point(x, y) {
    this.x = x;
    this.y = y;
}
// Methods
Point.prototype.dist = function () {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

Chúng ta có thể thấy rằng một constructor có hai phần. Thứ nhất, hàm Point thiết lập dữ liệu thực tế. Thứ hai, thuộc tính Point.prototype chứa một đối tượng với các phương thức. Dữ liệu trước đó là cụ thể cho mỗi thực tế, trong khi dữ liệu sau được chia sẻ giữa tất cả các thực tế.

Để sử dụng Point, chúng ta gọi nó qua toán tử mới:

> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301

p là một trường hợp của Point:

> p instanceof Point
true

Mảng

Mảng là chuỗi các phần tử có thể truy cập thông qua các chỉ số số nguyên bắt đầu từ 0.

Array Literals

Array literal rất hữu ích để tạo ra các mảng:

> var arr = [ 'a', 'b', 'c' ];

Mảng trước có ba yếu tố: các chuỗi a, bc. Bạn có thể truy cập chúng thông qua chỉ số số nguyên:

> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]

Thuộc tính length cho biết một mảng có bao nhiêu phần tử. Bạn có thể sử dụng nó để thêm các phần tử và loại bỏ các phần tử:

> var arr = ['a', 'b'];
> arr.length
2

> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3

> arr.length = 1;
> arr
[ 'a' ]

Động số in cũng hoạt động cho mảng:

> var arr = [ 'a', 'b', 'c' ];
> 1 in arr // is there an element at index 1?
true
> 5 in arr // is there an element at index 5?
false

Lưu ý rằng mảng là đối tượng và do đó có thể có thuộc tính đối tượng:

> var arr = [];
> arr.foo = 123;
> arr.foo
123

Phương pháp mảng

Các mảng có nhiều phương thức (xem phương thức mẫu mảng). Dưới đây là một vài ví dụ:

> var arr = [ 'a', 'b', 'c' ];

> arr.slice(1, 2)  // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]

> arr.push('x')  // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]

> arr.pop()  // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]

> arr.shift()  // remove first element
'a'
> arr
[ 'b', 'c' ]

> arr.unshift('x')  // prepend an element
3
> arr
[ 'x', 'b', 'c' ]

> arr.indexOf('b')  // find the index of an element
1
> arr.indexOf('y')
-1

> arr.join('-')  // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'

Lặp lại trên Arrays

Có một số phương pháp mảng để lặp lại trên các phần tử (xem Iteration (Non-destructive)). Hai phương pháp quan trọng nhất là forEach và map.

forEach lặp lại trên một mảng và trao phần tử hiện tại và chỉ mục của nó cho một hàm:

[ 'a', 'b', 'c' ].forEach(
    function (elem, index) {  // (1)
        console.log(index + '. ' + elem);
    });

Mã trước đó tạo ra đầu ra sau:

0. a
1. b
2. c

Lưu ý rằng hàm trong dòng (1) được tự do bỏ qua các đối số.

map tạo một mảng mới bằng cách áp dụng một hàm cho mỗi phần tử của mảng hiện có:

> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]

Các biểu thức thường xuyên

JavaScript có hỗ trợ tích hợp cho các biểu thức đều đặn.

/^abc$/
/[A-Za-z0-9]+/

Kiểm tra phương pháp: Có phù hợp không?

> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false

Phương pháp exec ((): Nhóm khớp và chụp

> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]

Mảng được trả lại chứa sự khớp hoàn chỉnh ở chỉ số 0, chụp nhóm đầu tiên ở chỉ số 1, v.v. Có một cách (được thảo luận trong RegExp.prototype.exec: Capture Groups) để gọi phương thức này nhiều lần để có được tất cả các sự khớp.

Phương pháp thay thế ((): Tìm kiếm và thay thế

> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'

Các tham số đầu tiên của thay thế phải là một biểu thức thường xuyên với một cờ /g; nếu không, chỉ có sự xuất hiện đầu tiên được thay thế.

Toán học

Toán học là một đối tượng với các hàm số học.

> Math.abs(-2)
2

> Math.pow(3, 2)  // 3 to the power of 2
9

> Math.max(2, -1, 5)
5

> Math.round(1.9)
2

> Math.PI  // pre-defined constant for π
3.141592653589793

> Math.cos(Math.PI)  // compute the cosine for 180°
-1

Thêm nữa