Gọi một hàm trong javascript. Năm cách để gọi một hàm

Chức năng

Hàm là một khối mã JavaScript được xác định một lần và có thể được thực thi hoặc gọi nhiều lần. Có thể bạn đã quen với khái niệm hàm dưới một tên khác, chẳng hạn như chương trình con hoặc thủ tục. Hàm có thể có tham số: Định nghĩa hàm có thể bao gồm danh sách các mã định danh, được gọi là tham số và đóng vai trò như các biến cục bộ trong phần nội dung của hàm.

Khi gọi hàm, chúng có thể được truyền các giá trị hoặc đối số tương ứng với tham số của chúng. Các hàm thường sử dụng các đối số của chúng để tính giá trị trả về, là giá trị của biểu thức gọi hàm. Ngoài các đối số, khi bất kỳ hàm nào được gọi, nó sẽ được truyền thêm một giá trị xác định ngữ cảnh của lệnh gọi - giá trị trong từ khóa this.

Các hàm trong JavaScript là các đối tượng và có thể được sử dụng theo nhiều cách khác nhau. Ví dụ, các hàm có thể được gán cho các biến và được truyền cho các hàm khác. Vì hàm là đối tượng nên bạn có thể gán giá trị cho thuộc tính của chúng và thậm chí gọi các phương thức của chúng.

JavaScript cho phép các định nghĩa hàm được lồng trong các hàm khác và các hàm như vậy sẽ có quyền truy cập vào tất cả các biến có trong phạm vi định nghĩa.

Xác định hàm

Định nghĩa hàm bắt đầu bằng từ khóa hàm, theo sau là các thành phần sau:

Một mã định danh chỉ định tên của hàm

Tên là một phần bắt buộc của câu lệnh khai báo hàm: nó sẽ được sử dụng để tạo một biến mới mà đối tượng hàm mới sẽ được gán. Trong các biểu thức định nghĩa hàm, tên có thể không có: nếu có, tên sẽ chỉ đề cập đến đối tượng hàm trong chính phần thân của hàm.

Một cặp dấu ngoặc đơn xung quanh danh sách không hoặc nhiều số nhận dạng, được phân tách bằng dấu phẩy

Các mã định danh này sẽ xác định tên của các tham số hàm và có thể được sử dụng làm biến cục bộ trong thân hàm.

Một cặp dấu ngoặc nhọn có 0 hoặc nhiều lệnh JavaScript bên trong

Các lệnh này tạo nên phần thân của hàm: chúng được thực thi mỗi khi hàm được gọi.

Ví dụ sau đây cho thấy một số định nghĩa hàm ở dạng câu lệnh và biểu thức. Lưu ý rằng các định nghĩa hàm dưới dạng biểu thức chỉ hữu ích nếu chúng là một phần của các biểu thức lớn hơn, chẳng hạn như lệnh gán hoặc lệnh gọi hàm, thực hiện điều gì đó với hàm mới được khai báo:

// In tên và giá trị của tất cả các thuộc tính của đối tượng obj function printprops(obj) ( for(var p in obj) console.log(p + ": " + obj[p] + "\n"); ) // Tính khoảng cách giữa các điểm (x1,y1) và (x2,y2) hàm distance(x1, y1, x2, y2) ( var dx = x2 - x1; var dy = y2 - y1; return Math.sqrt(dx *dx + dy*dy ); ) // Hàm đệ quy (gọi chính nó) tính hàm giai thừa giai thừa(x) ( if (x

Lưu ý rằng trong các biểu thức định nghĩa hàm, tên hàm có thể không xuất hiện. Câu lệnh khai báo hàm thực sự khai báo một biến và gán cho nó một đối tượng hàm.

Mặt khác, một biểu thức định nghĩa hàm không khai báo một biến. Tuy nhiên, trong các biểu thức định nghĩa, có thể chỉ định tên của hàm, như trong hàm giai thừa ở trên, có thể được yêu cầu trong phần nội dung của hàm để gọi chính nó. Nếu một biểu thức định nghĩa hàm bao gồm một tên thì tên đó sẽ đề cập đến một đối tượng hàm trong phạm vi của hàm đó. Trong thực tế, tên hàm trở thành một biến cục bộ, chỉ có thể truy cập được trong thân hàm. Trong hầu hết các trường hợp, tên hàm không cần phải được chỉ định trong biểu thức định nghĩa, làm cho các định nghĩa trở nên gọn gàng hơn.

Lưu ý rằng hầu hết (nhưng không phải tất cả) các hàm trong ví dụ đều chứa câu lệnh return. Câu lệnh return kết thúc hàm và trả về giá trị biểu thức của nó (nếu được chỉ định) cho chương trình gọi. Nếu không có biểu thức nào trong câu lệnh return, nó sẽ trả về không xác định. Nếu không có câu lệnh return trong hàm, trình thông dịch sẽ chỉ thực thi tất cả các câu lệnh trong thân hàm và trả về phần không xác định cho chương trình gọi.

Hầu hết các hàm trong ví dụ đều đánh giá một giá trị nào đó và sử dụng câu lệnh return để trả về giá trị đó cho chương trình gọi. Hàm printprops() hơi khác một chút theo nghĩa này: công việc của nó là in tên các thuộc tính của một đối tượng. Nó không cần trả về bất kỳ giá trị nào nên không có câu lệnh return trong hàm. Hàm printprops() sẽ luôn trả về giá trị không xác định. (Các hàm không có giá trị trả về đôi khi được gọi là thủ tục.)

Chức năng gọi

Mã chương trình tạo nên phần thân của hàm được thực thi không phải tại thời điểm hàm được xác định mà tại thời điểm nó được gọi. Các cuộc gọi hàm được thực hiện bằng cách sử dụng biểu thức cuộc gọi. Biểu thức cuộc gọi bao gồm một biểu thức gọi hàm trả về một đối tượng hàm, theo sau là dấu ngoặc đơn có danh sách 0 hoặc nhiều biểu thức đối số được phân tách bằng dấu phẩy bên trong.

Nếu biểu thức gọi hàm là biểu thức gọi thuộc tính - nếu hàm là thuộc tính đối tượng hoặc phần tử mảng (nghĩa là một phương thức) - thì biểu thức gọi là biểu thức gọi phương thức. Đoạn mã sau đây hiển thị một số ví dụ về các biểu thức gọi hàm phổ biến:

Printprops((x:4, tuổi: 24)); var d = khoảng cách (1,1,5,6); var f = giai thừa(5) / giai thừa(12); f = hình vuông(5);

Khi một hàm được gọi, tất cả các biểu thức đối số (được chỉ định giữa các dấu ngoặc đơn) đều được đánh giá và các giá trị kết quả được sử dụng làm đối số cho hàm. Các giá trị này được gán cho các tham số có tên được liệt kê trong định nghĩa hàm. Trong phần thân hàm, các biểu thức gọi tham số trả về giá trị của các đối số tương ứng.

Khi một hàm bình thường được gọi, giá trị trả về của hàm sẽ trở thành giá trị của biểu thức gọi. Nếu một hàm trả về sau khi trình thông dịch đã kết thúc, hàm không xác định sẽ được trả về. Nếu một hàm trả về do câu lệnh return, giá trị của biểu thức theo sau câu lệnh return sẽ được trả về hoặc không xác định nếu câu lệnh return không có biểu thức.

Một phương thức không là gì ngoài một hàm được lưu trữ như một thuộc tính của một đối tượng. Nếu bạn có hàm func và một đối tượng obj, thì bạn có thể định nghĩa một phương thức trên obj được gọi là phương thức như dưới đây:

// Định nghĩa một đối tượng và hàm đơn giản var obj = (); function func(a, b) ( return a+b;) // Thêm một phương thức vào đối tượng obj obj.method = func; // Bây giờ bạn có thể gọi phương thức này var result = obj.method(4, 5);

Thông thường, khi gọi các phương thức, hình thức truy cập thuộc tính bằng toán tử dấu chấm được sử dụng, nhưng bạn cũng có thể sử dụng hình thức truy cập thuộc tính bằng dấu ngoặc vuông. Ví dụ: cả hai biểu thức sau đây đều là biểu thức gọi phương thức:

Kết quả = obj.method(4, 5); kết quả = obj["phương thức"](4, 5);

Các đối số và giá trị trả về của lệnh gọi phương thức được xử lý theo cách giống hệt như lệnh gọi hàm thông thường. Tuy nhiên, lệnh gọi phương thức có một điểm khác biệt quan trọng: ngữ cảnh của lệnh gọi. Biểu thức truy cập thuộc tính bao gồm hai phần: một đối tượng (trong trường hợp này là obj) và tên thuộc tính (phương thức). Trong các biểu thức gọi phương thức như vậy, obj trở thành ngữ cảnh gọi và phần thân hàm có thể tham chiếu đối tượng đó bằng từ khóa this. Ví dụ:

Var obj = ( x: 0, y: 0, // Cộng phương thức: function(a, b) ( this.x = a; this.y = b; ), // Phương thức khác tính tổng: function() ( return this .x + this.y ) ); // Gọi các phương thức obj.add(15, 4); console.log(obj.sum()); // 19

Các phương thức và từ khóa this là trọng tâm của mô hình lập trình hướng đối tượng. Bất kỳ hàm nào được sử dụng làm phương thức đều thực sự nhận được một đối số ngầm định - đối tượng mà hàm đó được gọi. Thông thường, các phương thức thực hiện một số thao tác trên một đối tượng và cú pháp gọi phương thức phản ánh rõ ràng thực tế là hàm đó hoạt động trên đối tượng.

Xin lưu ý: đây là từ khóa, không phải tên biến hoặc thuộc tính. Cú pháp JavaScript không cho phép phần tử này được gán một giá trị.

Đối số và tham số của hàm

Trong JavaScript, các định nghĩa hàm không chỉ định loại tham số và lệnh gọi hàm không thực hiện bất kỳ việc kiểm tra kiểu nào đối với các giá trị đối số được truyền. Trên thực tế, JavaScript thậm chí không kiểm tra số lượng đối số khi gọi hàm. Các phần phụ bên dưới mô tả điều gì sẽ xảy ra nếu số lượng đối số trong lệnh gọi hàm nhỏ hơn hoặc lớn hơn số lượng tham số được khai báo. Chúng cũng trình bày cách bạn có thể kiểm tra rõ ràng các loại đối số của hàm nếu bạn cần đảm bảo rằng hàm đó không được gọi với các đối số không hợp lệ.

Đối số tùy chọn

Khi số lượng đối số trong lệnh gọi hàm nhỏ hơn số lượng tham số được khai báo, các đối số bị thiếu sẽ được đặt thành không xác định. Việc viết hàm thường thuận tiện sao cho một số đối số là tùy chọn và có thể được bỏ qua khi gọi hàm. Trong trường hợp này, mong muốn cung cấp khả năng gán các giá trị mặc định hợp lý hợp lý cho các tham số có thể bị bỏ qua. Ví dụ:

// Thêm vô số tên của // thuộc tính của đối tượng obj vào mảng arr và trả về nó. Nếu đối số // arr không được thông qua, hãy tạo và trả về một mảng mới hàm getPropertyNames(obj, /* tùy chọn */ arr) ( if (arr === und xác định) arr = ; // Nếu mảng không được xác định, hãy tạo một thuộc tính for( var mới trong obj) arr.push(property); return arr; // Hàm này có thể được gọi với 1 hoặc 2 đối số: var a = getPropertyNames((x:1, y:1)); // Lấy các thuộc tính của đối tượng trong một mảng mới getPropertyNames((z:5),a); // thêm các thuộc tính của đối tượng mới vào mảng này console.log(a); // ["XYZ"]

Lưu ý rằng khi khai báo hàm, các đối số tùy chọn phải hoàn thành danh sách đối số thì mới có thể bỏ qua. Lập trình viên sẽ viết lệnh gọi đến hàm của bạn sẽ không thể truyền đối số thứ hai và đồng thời bỏ qua đối số thứ nhất: anh ta sẽ buộc phải chuyển rõ ràng giá trị không xác định trong đối số đầu tiên. Cũng lưu ý chú thích /* tùy chọn */ trong định nghĩa hàm, trong đó nhấn mạnh thực tế rằng tham số là tùy chọn.

Danh sách đối số có độ dài thay đổi

Nếu số lượng đối số trong lệnh gọi hàm vượt quá số lượng tên tham số thì hàm đó không thể truy cập trực tiếp vào các giá trị chưa được đặt tên. Giải pháp cho vấn đề này được cung cấp bởi đối tượng Arguments. Trong nội dung hàm, mã định danh đối số đề cập đến đối tượng Đối số có trong lệnh gọi. Đối tượng Arguments là một đối tượng giống như mảng cho phép các giá trị được truyền cho một hàm được truy xuất bằng số thay vì tên của chúng.

Giả sử rằng hàm func đã được xác định và yêu cầu một đối số x. Nếu bạn gọi hàm này bằng hai đối số, đối số đầu tiên sẽ có sẵn bên trong hàm bằng tên của tham số x hoặc dưới dạng đối số. Đối số thứ hai sẽ chỉ có sẵn dưới dạng đối số. Ngoài ra, giống như mảng thực, các đối số có thuộc tính độ dài chỉ định số phần tử mà nó chứa. Nghĩa là, trong phần thân của hàm có tên func có hai đối số, đối số.length có giá trị 2.

Đối tượng Arguments có thể được sử dụng cho nhiều mục đích khác nhau. Ví dụ sau đây cho thấy cách sử dụng nó để kiểm tra xem một hàm có được gọi với số lượng đối số chính xác hay không, điều mà JavaScript sẽ không làm được cho bạn:

Hàm func(x, y, z) ( // Đầu tiên kiểm tra xem số lượng đối số có được truyền đúng hay không if (arguments.length != 3) ( Throw new Error("Func được gọi bằng " + đối số.length + " đối số và bắt buộc 3."); ) // Và bây giờ chính mã hàm...)

Lưu ý rằng thường không cần thiết phải kiểm tra số lượng đối số, như trong ví dụ này. Hành vi mặc định của trình thông dịch JavaScript phù hợp với hầu hết các trường hợp: các đối số bị thiếu được thay thế bằng giá trị không xác định và các đối số bổ sung sẽ bị bỏ qua.

Đối tượng Arguments minh họa một tính năng quan trọng của hàm JavaScript: chúng có thể được viết để nhận bất kỳ số lượng đối số nào. Hàm sau đây nhận bất kỳ số lượng đối số nào và trả về giá trị của đối số lớn nhất (hàm Math.max() tích hợp hoạt động tương tự):

Hàm maxNumber() ( var m = Number.NEGATIVE_INFINITY; // Lặp qua tất cả các đối số, tìm và // lưu trữ đối số lớn nhất for(var i = 0; i m) m = đối số[i]; // Trả về giá trị lớn nhất return m ; ) var lớn nhất = maxNumber(1, 10, 100, 2, 3, 1000, 4, 5, 10000, 6); // 10000

Các hàm như thế này có thể nhận số lượng đối số tùy ý được gọi là hàm biến đổi, hàm phân thức biến đổi hoặc hàm varargs. Thuật ngữ này nảy sinh cùng với sự ra đời của ngôn ngữ lập trình C.

Lưu ý rằng không được phép gọi các hàm variadic với danh sách đối số trống. Sẽ hoàn toàn hợp lý khi sử dụng đối tượng đối số khi viết một hàm yêu cầu một số lượng cố định các đối số được đặt tên bắt buộc, theo sau là một số lượng đối số không được đặt tên tùy ý.

Đừng quên rằng đối số thực tế không phải là một mảng - đó là đối tượng Đối số. Mỗi đối tượng Arguments có các phần tử mảng được đánh số và thuộc tính độ dài, nhưng về mặt kỹ thuật không phải là một mảng. Tốt hơn nên coi nó như một đối tượng có một số thuộc tính được đánh số.

Ngoài các phần tử mảng của nó, đối tượng Arguments còn xác định các thuộc tính của callee và caller. Việc cố gắng thay đổi giá trị của các thuộc tính này trong chế độ nghiêm ngặt của ECMAScript 5 đảm bảo sẽ tạo ra ngoại lệ TypeError. Tuy nhiên, ở chế độ lỏng lẻo, tiêu chuẩn ECMAScript nêu rõ rằng thuộc tính callee đề cập đến hàm hiện đang thực thi. Thuộc tính người gọi không phải là thuộc tính tiêu chuẩn, nhưng nó có trong nhiều triển khai và đề cập đến hàm được gọi là hàm hiện tại.

Thuộc tính người gọi có thể được sử dụng để truy cập vào ngăn xếp cuộc gọi và thuộc tính callee đặc biệt hữu ích để gọi đệ quy các hàm chưa được đặt tên:

Biến giai thừa = hàm(x) ( if (x

Thuộc tính và phương thức của hàm

Chúng ta đã thấy rằng các hàm có thể được sử dụng làm giá trị trong các chương trình JavaScript. Toán tử typeof trả về chuỗi "hàm" cho các hàm, nhưng hàm thực sự là một loại đối tượng đặc biệt trong JavaScript. Và vì hàm là đối tượng nên chúng có các thuộc tính và phương thức giống như bất kỳ đối tượng nào khác. Thậm chí còn có hàm tạo Function() để tạo các đối tượng hàm mới. Các phần phụ sau đây mô tả các thuộc tính và phương thức của hàm.

thuộc tính chiều dài

Trong nội dung hàm, thuộc tính đối số.length chỉ định số lượng đối số được truyền cho hàm. Tuy nhiên, thuộc tính độ dài của hàm lại có ý nghĩa khác. Thuộc tính chỉ đọc này trả về số lượng đối số mà hàm mong đợi nhận được - số lượng tham số đã khai báo.

Đoạn mã sau định nghĩa một hàm có tên check() nhận một mảng đối số từ một hàm khác. Nó so sánh thuộc tính đối số.length (số lượng đối số thực sự được truyền) với thuộc tính đối số.callee.length (số lượng đối số dự kiến) để xác định xem hàm có được truyền nhiều đối số như mong đợi hay không. Nếu các giá trị không khớp, một ngoại lệ sẽ được đưa ra. Tiếp theo hàm check() là hàm kiểm tra, func(), minh họa cách sử dụng hàm check():

// Hàm này sử dụng đối số.callee, vì vậy nó // sẽ không hoạt động ở chế độ nghiêm ngặt function check(args) ( var Real = args.length; // Số lượng đối số thực tế var mong đợi = args.callee.length; // Dự kiến số đối số if (thực tế !== dự kiến) // Nếu chúng không khớp, một ngoại lệ sẽ được đưa ra ném Lỗi mới("mong đợi: " + dự kiến ​​+ "; đã nhận " + thực tế ) function func(x, y, z) ( // Kiểm tra số lượng đối số dự kiến ​​và thực tế đã vượt qua check(arguments); // Bây giờ thực hiện phần còn lại của hàm return x + y + z;

thuộc tính nguyên mẫu

Mọi hàm đều có một thuộc tính nguyên mẫu đề cập đến một đối tượng được gọi là đối tượng nguyên mẫu. Mỗi hàm có đối tượng nguyên mẫu riêng. Khi một hàm được sử dụng làm hàm tạo, đối tượng mới được tạo sẽ kế thừa các thuộc tính của đối tượng nguyên mẫu đó.

Nguyên mẫu và thuộc tính nguyên mẫu đã được thảo luận trong bài viết trước.

phương thức call() và apply()

Các phương thức call() và apply() cho phép bạn gọi một hàm một cách gián tiếp, như thể nó là một phương thức trên một số đối tượng khác. Đối số đầu tiên cho cả hai phương thức call() và apply() là đối tượng mà hàm được gọi trên đó; đối số này chỉ định ngữ cảnh của lệnh gọi và trở thành giá trị của từ khóa this trong nội dung hàm. Để gọi func() (không có đối số) làm phương thức của obj, bạn có thể sử dụng bất kỳ phương thức call() hoặc apply() nào:

Func.call(obj); func.apply(obj);

Cả hai cách gọi nó đều tương đương với đoạn mã sau (giả sử obj không có thuộc tính có tên m):

Obj.m = func; // Tạm thời biến func thành một phương thức obj obj.m(); // Gọi nó mà không cần đối số. xóaobj.m; // Loại bỏ phương thức tạm thời.

Trong chế độ nghiêm ngặt của ECMAScript 5, đối số đầu tiên của phương thức call() và apply() trở thành giá trị của this, ngay cả khi đó là giá trị đơn giản, null hoặc không xác định. Trong ECMAScript 3 và ở chế độ lỏng lẻo, các giá trị null và không xác định được thay thế bằng đối tượng chung và giá trị đơn giản được thay thế bằng đối tượng trình bao bọc tương ứng.

Tất cả các đối số khác cho phương thức call() theo sau đối số đầu tiên chỉ định bối cảnh gọi sẽ được chuyển đến hàm được gọi. Phương thức apply() hoạt động giống như phương thức call(), ngoại trừ việc các đối số của hàm được truyền dưới dạng một mảng. Nếu một hàm có khả năng xử lý số lượng đối số tùy ý, thì phương thức apply() có thể được sử dụng để gọi hàm đó trong ngữ cảnh của một mảng có độ dài tùy ý.

Ví dụ sau minh họa cách sử dụng thực tế của phương thức call():

// Dưới đây là hai hàm hiển thị các thuộc tính và // giá trị thuộc tính của một đối tượng tùy ý. Phương thức // các màn hình được truyền dưới dạng đối số func function print1(func, obj) ( for (n in obj) func(n +": " + obj[n]); ) function print2(func, objDevice, obj) ( for ( n in obj) func.call(objDevice, n +": " + obj[n] ) var obj = (x:5, y:10); print2(document.write, document, obj); // Hoạt động chính xác print2(console.log, console, obj); print1(document.write, obj); // Một ngoại lệ gọi bất hợp pháp sẽ xảy ra vì print1(console.log, obj); // không thể gọi các phương thức này nếu không có đối tượng ngữ cảnh

phương thức liên kết ()

Phương thức bind() lần đầu tiên xuất hiện trong ECMAScript 5 nhưng rất dễ bắt chước trong ECMAScript 3. Như tên gọi của nó, mục đích chính của phương thức bind() là liên kết một hàm với một đối tượng. Nếu bạn gọi phương thức bind() của func và truyền cho nó một đối tượng obj, nó sẽ trả về một hàm mới. Việc gọi hàm mới (như một hàm thông thường) sẽ gọi hàm ban đầu func như một phương thức trên obj. Mọi đối số được truyền cho hàm mới sẽ được chuyển đến hàm ban đầu. Ví dụ:

// Hàm liên kết hàm func(y) ( return this.x + y; ) var obj = (x:1); // Đối tượng cần liên kết với var g = func.bind(obj); // Gọi g(x) sẽ gọi obj.func(x)

Kiểu liên kết này rất dễ triển khai trong ECMAScript 3, như minh họa bên dưới:

// Trả về một hàm gọi func như một phương thức của đối tượng obj // và truyền cho nó tất cả các đối số của nó function bind(func, obj) ( if (func.bind) return func.bind(obj); // Sử dụng phương thức liên kết if có sẵn khác return function() ( // Nếu không thì liên kết như bên dưới return func.apply(obj, đối số); )

Phương thức bind() trong ECMAScript 5 không chỉ liên kết một hàm với một đối tượng. Nó cũng thực hiện truyền một phần: ngoài giá trị this, tất cả các đối số được truyền cho phương thức bind() sau đối số đầu tiên của nó sẽ bị ràng buộc. Ứng dụng một phần là một kỹ thuật phổ biến trong lập trình chức năng và đôi khi được gọi là cà ri.

Hãy bắt đầu với thực tế là ngôn ngữ JavaScript hỗ trợ khái niệm OOP (lập trình hướng đối tượng). Khái niệm này là có những phần tử như đối tượng và những đối tượng này có nhiều thuộc tính và phương thức (hàm) khác nhau cho phép bạn thao tác với chúng.

Hàm là một khối mã riêng biệt bao gồm một hoặc nhiều câu lệnh. Nó có tên (duy nhất) riêng và có thể chấp nhận các tham số khác nhau, tùy thuộc vào tham số nào mà nó có thể thực hiện một thao tác cụ thể.

Một phương thức cũng là một hàm, nhưng nó đã thuộc về một lớp hoặc đối tượng nào đó.

Để gọi một phương thức, trước tiên bạn phải viết tên đối tượng, sau đó viết tên phương thức cách nhau bằng dấu chấm. Ngoại lệ đối với quy tắc này là khi gọi các phương thức notification(), confirm(), và nhắc() của đối tượng window. Chúng có thể được gọi mà không cần chỉ định tên đối tượng. Chúng ta đã làm quen với các phương pháp này trong bài viết này.

Ngoài ra, trong các bài viết trước chúng ta đã được giới thiệu về phương thức xuất document.write(), thuộc về đối tượng document.

Vì vậy, trong lập trình có một cơ hội rất quan trọng, đó là bạn có thể tạo ra các hàm của riêng mình.

Cú pháp hàm trông như thế này:


Ví dụ: hãy tạo một hàm đơn giản để thêm văn bản được truyền vào một đoạn văn và hiển thị nó. Và nó cũng sẽ làm cho nó được in đậm và in nghiêng.

Hàm writeText(text)( //Thêm văn bản vào đoạn văn và hiển thị nó document.write("

"+văn bản+"

"); ) //Gọi hàm vừa tạo writeText("Xin chào!");

Lưu tài liệu và mở nó trong trình duyệt.


Bình luận! Khi khai báo một hàm, dù có bao nhiêu toán tử cũng phải có dấu ngoặc nhọn.

Tại sao cần có các hàm trong lập trình?

Ưu điểm chính của việc sử dụng chức năng này là giảm kích thước của mã nguồn tập lệnh.

Giả sử chúng ta cần lặp lại ba mảng một chiều. Như chúng ta đã biết từ bài viết này: , mảng được lặp bằng vòng lặp. Nếu không có chức năng này, mã cho tập lệnh này sẽ trông như thế này:

//khai báo 3 mảng var Array1 = ; var Array2 = ["b", 5, 9.2, "h", 8, 2]; var Array2 = ; for(var i = 0; i< arr1.length; i++){ document.write("

Phần tử mảng arr1, có chỉ số " + i + " bằng: "+ arr1[i] +"

"); ) for(var i = 0; i< arr2.length; i++){ document.write("

Phần tử mảng arr2, có chỉ số " + i + " bằng: "+ arr2[i] +"

"); ) for(var i = 0; i< arr3.length; i++){ document.write("

Phần tử mảng arr3, có chỉ số " + i + " bằng: "+ arr3[i] +"

"); }

Vì vậy, để không phải viết vòng lặp của riêng mình cho từng mảng, tốt hơn hết bạn nên sử dụng một hàm trong đó chúng ta truyền mảng đó và nó sẽ hiển thị tất cả các phần tử của nó trên màn hình. Bằng cách này, trước tiên, chúng tôi giảm kích thước mã và thứ hai, chúng tôi loại bỏ việc lặp lại mã.

Hàm printArr(arr)( for(var i = 0; i< arr.length; i++){ document.write("

Phần tử mảng có chỉ số " + i + " bằng: "+ arr[i] +"

"); ) ) //khai báo ba mảng var arr1 = ; var arr2 = ["b", 5, 9.2, "h", 8, 2]; var arr2 = ; //Gọi hàm đã tạo để lặp qua từng mảng printArr(arr1); printArr(arr2); printArr(arr3);

Thông số chức năng

Một hàm có thể nhận bất kỳ số lượng tham số nào, từ một đến vô cùng. Hoặc, nó có thể hoàn toàn không có tham số.

Hãy tạo một hàm không tham số chỉ đơn giản in cụm từ "Xin chào thế giới" cổ điển ra màn hình.

Hàm helloWorld())( document.write("Hello World"); ) //Gọi hàm không có tham số, helloWorld helloWorld();

Bất kỳ tham số hàm nào cũng có thể có giá trị mặc định riêng. Điều này có nghĩa là nếu chúng ta không truyền bất kỳ giá trị nào cho tham số này khi gọi hàm thì nó sẽ sử dụng giá trị mặc định của nó.

Ví dụ: hãy tạo một hàm cộng hai số đã truyền. Nếu chúng ta chỉ truyền một số thì theo mặc định, số thứ hai sẽ là 4.

Hàm summa(number1, number2 = 4)( document.write("

Tổng của các số " + number1 + "(Tham số đầu tiên) và " + number2 + "(Tham số thứ hai) bằng: " + (number1 + number2) + "

"); ) //Gọi một hàm, theo mặc định, sẽ đưa ra kết quả của việc cộng số đã truyền, với số 4. summa(5); // Kết quả: 9 //Nếu chúng ta cũng cung cấp tham số thứ hai, thì hàm sẽ đưa ra kết quả của việc cộng các số từ cả hai tham số .summa(5, 20); // Kết quả: 25);

Cũng có thể bên trong một hàm người ta có thể gọi một hàm khác hiện có.

Ví dụ: hãy gọi hàm đầu tiên mà chúng ta đã tạo, writeText(), bên trong hàm summa() trước đó. Chúng ta sẽ chuyển kết quả của việc cộng số vào hàm writeText(). Trong trường hợp này, mã cho hàm summa() sẽ như sau:

Hàm summa(number1, number2 = 4)( writeText(number1 + number2); ) //Gọi hàm summa summa(5); // Kết quả: 9 summa(5, 20); // Kết quả: 25

Hàm trả về một giá trị nào đó

Cho đến nay chúng ta đã viết được các hàm hiển thị kết quả ra màn hình ngay lập tức.

Bây giờ hãy học cách viết một hàm trả về một kết quả nào đó. Chúng ta có thể thêm kết quả này vào một số biến và làm việc với nó hơn nữa.

Để hiểu rõ hơn những gì chúng ta đang nói đến, hãy nhớ các phương thức như nhắc() và confirm(). Các phương thức này thực sự trả về giá trị nhận được từ người dùng, thay vì hiển thị nó.

Ví dụ: hãy tạo hàm riêng để trả về phần tử cuối cùng của mảng được truyền dưới dạng tham số.

Function LastElement(arr)( //Trả về phần tử cuối cùng của mảng đã truyền return arr; ) //Khai báo mảng var otherArr = ["iphone", "asus", 2000, 9.8, "twix"]; //Gọi hàm LastElement đã tạo và truyền mảng đã tạo dưới dạng tham số otherArr var LastEl = LastElement(otherArr); //Hiển thị phần tử cuối cùng của mảng notification(lastEl);

Kết quả chúng ta sẽ nhận được từ 'twix', vì từ này là phần tử cuối cùng của mảng otherArr.

Phương thức Alert() không trả về bất cứ thứ gì. Nghĩa là, nếu chúng ta cố gắng hiển thị một biến có kiểu chứa kết quả của việc gọi phương thức notification(), chúng ta sẽ thấy giá trị unexpected . Điều này cũng giống như việc cố gắng hiển thị giá trị của một biến trống.

Ví dụ: hãy lấy kết quả của lệnh gọi cuối cùng tới notification() từ ví dụ trước, đặt nó vào biến resAlert và sử dụng hàm writeText mà chúng ta đã tạo để thử in kết quả.

//Hiển thị phần tử cuối cùng nhận được của mảng var resAlert = notification(lastEl); thử nghiệm var; writeText(resAlert); // không xác định writeText(test); //không xác định

Như bạn có thể thấy, trong cả hai trường hợp, chúng tôi đều nhận được giá trị không xác định.

Biến toàn cục và cục bộ

Biến toàn cục là những biến được khai báo bên ngoài hàm. Nghĩa là, tất cả các biến không được khai báo bên trong hàm đều là biến toàn cục. Chúng hiển thị (hợp lệ) trong suốt tài liệu.

Biến cục bộ là những biến được khai báo trong chính hàm đó. Và chúng chỉ có giá trị trong một chức năng nhất định. Ngoài nó, các biến cục bộ sẽ không còn hoạt động.

Các biến cục bộ và toàn cục không liên quan đến nhau theo bất kỳ cách nào.


Trong ví dụ từ hình ảnh, nếu chúng ta cố in nội dung của biến x, chúng ta sẽ nhận được thông báo không xác định vì chúng ta quên gọi hàm other().

Vì vậy, để những thay đổi được thực hiện bên trong hàm có thể hoạt động thì cần phải gọi hàm này.

Chúng ta gọi hàm other() và nếu bây giờ chúng ta cố gắng hiển thị giá trị của biến x thì kết quả là số 4.

Để truy cập một biến toàn cục từ bên trong một hàm, bạn không cần phải làm gì cả, bạn chỉ cần sử dụng nó. Những thay đổi được thực hiện đối với các biến toàn cục sẽ hiển thị bên ngoài hàm.

Var x = 8; hàm tăng() ( x++; ) //Gọi hàm tăng() tăng(); cảnh báo(x); //Kết quả: 9

Nếu không muốn biến toàn cục thay đổi, chúng ta phải khai báo một biến cục bộ (có thể có cùng tên với biến toàn cục) và mọi hành động sẽ được thực hiện trên đó.

Var g = 100; function func())( var g = 14; g *= 2; // Điều này giống với g = g * 2 notification(g); // Kết quả: 28 ) // Gọi hàm. chức năng(); cảnh báo(g);//Kết quả: 100

Vậy thôi các bạn độc giả thân mến, bây giờ các bạn đã biết hàm là gì, cách tạo hàm của riêng mình, cách gọi hàm và tồn tại những loại hàm nào. Bạn cũng đã tìm hiểu biến toàn cục và biến cục bộ là gì.

Như tôi đã viết ở đầu bài viết, hàm là thành phần rất quan trọng, vì vậy bạn nên hiểu rõ về chúng.

Nhiệm vụ
  • Tạo một hàm lấy hai số làm tham số và trả về kết quả của phép nhân các số đó.
  • In kết quả.
  • 24 tháng 5 năm 2011 lúc 01:13 Năm cách gọi hàm
    • JavaScript
    • Dịch

    Tôi thường gặp những đoạn mã JavaScript có lỗi do hiểu sai cách hoạt động của các hàm trong JavaScript (nhân tiện, phần lớn mã này là do tôi viết). JavaScript là một ngôn ngữ đa mô hình và nó có các cơ chế lập trình chức năng. Đã đến lúc khám phá những khả năng này. Trong bài viết này, tôi sẽ cho bạn biết năm cách để gọi hàm trong JavaScript.

    Trong giai đoạn đầu học JavaScript, người mới bắt đầu thường nghĩ rằng các hàm trong nó hoạt động theo cách tương tự như trong C#. Nhưng cơ chế gọi hàm trong JavaScript có một số điểm khác biệt quan trọng và việc thiếu hiểu biết về chúng có thể dẫn đến những lỗi không dễ tìm thấy.

    Hãy viết một hàm đơn giản trả về một mảng gồm ba phần tử - giá trị this hiện tại và hai đối số được truyền cho hàm.
    hàm makeArray(arg1, arg2)( return [ this, arg1, arg2 ]; )

    Cách phổ biến nhất: gọi toàn cục Người mới bắt đầu thường khai báo các hàm như trong ví dụ trên. Gọi hàm này thật dễ dàng:
    makeArray("một", "hai"); // => [ cửa sổ, "một", "hai" ]
    Chờ đợi. Đối tượng cửa sổ đến từ đâu? Tại sao điều này bằng window ?

    Trong JavaScript, bất kể tập lệnh được thực thi trong trình duyệt hay trong môi trường khác, nó luôn được xác định đối tượng toàn cầu. Bất kỳ mã nào trong tập lệnh của chúng tôi không bị "ràng buộc" với bất kỳ thứ gì (nghĩa là nằm ngoài phần khai báo đối tượng) thực sự nằm trong ngữ cảnh của đối tượng toàn cục. Trong trường hợp của chúng tôi, makeArray không chỉ là một hàm “đi bộ” riêng lẻ. Trên thực tế, makeArray là một phương thức của đối tượng toàn cục (trong trường hợp thực thi mã trong trình duyệt) window . Thật dễ dàng để chứng minh:
    cảnh báo (typeof window.methodThatDoesntExist); // => cảnh báo không xác định(typeof window.makeArray); // => hàm
    Tức là gọi makeArray("one", "two"); tương đương với việc gọi window.makeArray("one", "two"); .

    Điều làm tôi buồn là đây là cách gọi hàm phổ biến nhất, bởi vì nó hàm ý sự hiện diện của một hàm toàn cục. Và tất cả chúng ta đều biết rằng các hàm và biến toàn cục không phải là dạng tốt nhất trong lập trình. Điều này đặc biệt đúng với JavaScript. Tránh các định nghĩa toàn cầu và bạn sẽ không hối tiếc.

    Quy tắc gọi hàm số 1: Nếu một hàm được gọi trực tiếp mà không chỉ định đối tượng (ví dụ: myFunction()), giá trị của hàm này sẽ là đối tượng chung (cửa sổ nếu mã được thực thi trong trình duyệt).

    Gọi một Phương thức Hãy tạo một đối tượng đơn giản và tạo phương thức makeArray cho nó. Hãy khai báo đối tượng bằng cách sử dụng ký hiệu chữ và sau đó gọi phương thức của chúng ta:
    // tạo một đối tượng var arrayMaker = ( someProperty: "some value", make: makeArray ); // gọi phương thức make() arrayMaker.make("one", "two"); // => [ arrayMaker, "one", "two" ] // cú pháp thay thế, sử dụng dấu ngoặc vuông arrayMaker["make"]("one", "two"); // => [ arrayMaker, "một", "hai" ]
    Bạn có thấy sự khác biệt? Giá trị của this trong trường hợp này là chính đối tượng đó. Tại sao không phải là window , như trong trường hợp trước, vì khai báo hàm không thay đổi? Bí mật nằm ở cách các hàm được truyền trong JavaScript. Hàm là một loại JavaScript tiêu chuẩn thực sự là một đối tượng và giống như bất kỳ đối tượng nào khác, các hàm có thể được truyền đi và sao chép. Trong trường hợp này, về cơ bản, chúng tôi đã sao chép toàn bộ hàm, bao gồm danh sách đối số và nội dung, đồng thời gán đối tượng kết quả cho thuộc tính make của đối tượng arrayMaker. Điều này tương đương với một tuyên bố như thế này:
    var arrayMaker = ( someProperty: "Some value"; make: function (arg1, arg2) ( return [ this, arg1, arg2]; ) );
    Quy tắc gọi hàm số 2: Trong hàm được gọi bằng cú pháp gọi phương thức, chẳng hạn như obj.myFunction() hoặc obj["myFunction"]() , giá trị này sẽ có giá trị obj .

    Việc hiểu sai nguyên tắc nhìn chung đơn giản này thường dẫn đến sai sót khi xử lý các sự kiện:
    nút chức năngClicked())( var text = (this === window) ? "window" : this.id; cảnh báo (văn bản); ) var nút1 = document.getElementById("btn1"); nút var2 = document.getElementById("btn2"); nút1.onclick = nút Đã nhấp; nút2.onclick = function())( nútClicked(); );
    Nhấp vào nút đầu tiên sẽ hiển thị một thông báo "btn1" bởi vì trong trường hợp này chúng ta đang gọi một hàm như một phương thức và cái này bên trong hàm sẽ lấy giá trị của đối tượng mà phương thức này thuộc về. Nhấp vào nút thứ hai sẽ cung cấp cho "cửa sổ" bởi vì trong trường hợp này chúng ta đang gọi trực tiếp nútClicked (tức là không giống như obj.buttonClicked()). Điều tương tự cũng xảy ra khi chúng ta gán một trình xử lý sự kiện cho thẻ phần tử, như trong trường hợp nút thứ ba. Nhấp vào nút thứ ba sẽ hiển thị thông báo tương tự như nút thứ hai.

    Khi sử dụng các thư viện như jQuery, bạn không cần phải suy nghĩ về điều này. jQuery sẽ chú ý viết lại giá trị này trong trình xử lý sự kiện để giá trị này là phần tử phát sinh sự kiện:
    // sử dụng jQuery $("#btn1").click(function() ( Alert(this.id); // jQuery sẽ đảm bảo "đây" là một nút ));
    jQuery quản lý như thế nào để thay đổi giá trị của this ? Đọc dưới đây.

    Hai cách nữa: apply() và call() Điều hợp lý là bạn càng sử dụng hàm thường xuyên thì bạn càng phải chuyển chúng và gọi chúng trong các ngữ cảnh khác nhau. Thường thì cần phải ghi đè giá trị của this . Nếu bạn còn nhớ, các hàm trong JavaScript là các đối tượng. Trong thực tế, điều này có nghĩa là các hàm có các phương thức được xác định trước. apply() và call() là hai trong số đó. Chúng cho phép bạn ghi đè giá trị này:
    var car = ( năm: 2008, model: "Dodge Bailout" ); makeArray.apply(xe, [ "một", "hai"]); // => [ car, "one", "two" ] makeArray.call(car, "one", "two"); // => [ ô tô, "một", "hai" ]
    Hai phương pháp này rất giống nhau. Tham số đầu tiên ghi đè lên điều này. Sự khác biệt giữa chúng nằm ở các đối số tiếp theo: Function.apply() chấp nhận một mảng các giá trị sẽ được truyền cho hàm, trong khi Function.call() chấp nhận các đối số riêng biệt. Trong thực tế, theo tôi, sử dụng apply() sẽ thuận tiện hơn.

    Quy tắc gọi hàm số 3: Nếu bạn muốn ghi đè giá trị của this mà không sao chép hàm sang đối tượng khác, bạn có thể sử dụng myFunction.apply(obj) hoặc myFunction.call(obj) .

    Hàm tạo Tôi sẽ không đi sâu vào chi tiết về việc khai báo các kiểu tùy chỉnh trong JavaScript, nhưng tôi nghĩ cần phải nhắc bạn rằng không có lớp nào trong JavaScript và bất kỳ kiểu tùy chỉnh nào cũng cần một hàm tạo. Ngoài ra, tốt hơn là khai báo các phương thức thuộc loại tùy chỉnh bằng cách sử dụng nguyên mẫu, đây là thuộc tính của hàm khởi tạo. Hãy tạo kiểu riêng của chúng ta:
    // khai báo hàm khởi tạo ArrayMaker(arg1, arg2) ( this.someProperty = "không quan trọng"; this.theArray = [ this, arg1, arg2 ]; ) // khai báo các phương thức ArrayMaker.prototype = ( someMethod: function () ( Alert( "Được gọi bởi someMethod"); getArray: function () ( return this.theArray; ) ); var am = new ArrayMaker("một", "hai"); var other = new ArrayMaker("đầu tiên", "thứ hai"); am.getArray(); // => [ sáng, "một", "hai" ]
    Điều quan trọng trong ví dụ này là sự hiện diện của toán tử mới trước lệnh gọi hàm. Nếu không có nó, nó sẽ là một lệnh gọi toàn cục và các thuộc tính được tạo trong hàm tạo sẽ thuộc về đối tượng chung. Chúng tôi không cần điều đó. Ngoài ra, các hàm tạo thường không trả về giá trị một cách rõ ràng. Nếu không có toán tử new hàm tạo sẽ trả về unfind , cùng với nó nó sẽ trả về this . Việc đặt tên hàm tạo bằng chữ in hoa được coi là phong cách tốt; Điều này sẽ nhắc nhở bạn về sự cần thiết của người điều hành mới.

    Nếu không, mã bên trong hàm tạo có thể sẽ giống với mã bạn viết bằng ngôn ngữ khác. Giá trị của this trong trường hợp này là đối tượng mới mà bạn đang tạo.

    Quy tắc gọi hàm số 4: Khi gọi một hàm bằng toán tử mới, giá trị của this sẽ là một đối tượng mới được tạo bởi thời gian chạy JavaScript. Nếu hàm này không trả về bất kỳ đối tượng nào một cách rõ ràng thì đối tượng này sẽ được trả về ngầm.

    Kết luận Hy vọng rằng, hiểu được sự khác biệt giữa các cách gọi hàm khác nhau sẽ giúp bạn cải thiện mã JavaScript của mình. Đôi khi các lỗi liên quan đến giá trị này rất khó phát hiện, vì vậy việc ngăn chặn chúng trước là điều hợp lý.

    Các hàm JavaScript cho phép bạn sắp xếp các tập lệnh và giúp việc sử dụng lại mã trở nên dễ dàng. Thay vì tạo ra những đoạn mã dài rải rác khắp trang HTML, các tập lệnh được sắp xếp thành các nhóm logic.

    Khai báo và gọi hàm JavaScript

    Cú pháp hàm JavaScript như sau:

    hàm ""name"" ("argument1"", ""argument2"", ""argument3"" ...) ( ""toán tử"" trả về ""giá trị"" )

    Tên xác định những gì chúng ta sẽ gọi hàm khi chúng ta gọi nó. Các đối số chỉ định các giá trị được truyền cho hàm để xử lý. Phần câu lệnh đại diện cho phần thân của hàm thực hiện việc xử lý. Câu lệnh return tùy chọn cho phép bạn trả về một giá trị.

    Ví dụ sau đây cho thấy một hàm được xác định trong phần của trang HTML và được gọi trong phần đó:

    hàm sayHello() ( cảnh báo("Xin chào!"); ) sayHello();

    Truyền đối số cho hàm

    Trong ví dụ trên (hàm JavaScript văn bản loại tập lệnh), không có đối số nào được truyền cho hàm. Thông thường, một hàm được thiết kế để thực hiện một số hành động với một số đối số:

    Một ví dụ đơn giản về hàm JavaScript sayHello(day, tháng) ( cảnh báo("Xin chào! Hôm nay là " + ngày + " " + tháng); ) sayHello("24", "July"); sayHello("1", "Tháng 8"); sayHello("24", "May");

    Trong ví dụ này, hàm gọi lại JavaScript được gọi nhiều lần, lấy các đối số sau đó được sử dụng để tạo một chuỗi hiển thị trong hộp thoại. Để thực hiện việc này mà không có chức năng, bạn sẽ phải lặp lại tập lệnh trong phần này ba lần. Rõ ràng, sử dụng hàm là một cách tiếp cận hiệu quả hơn.

    Trả về một giá trị từ một hàm

    Câu lệnh return được sử dụng để trả về một giá trị từ một hàm và sử dụng nó tại nơi hàm được gọi. Ví dụ, chúng ta sẽ khai báo một hàm cộng hai đối số và trả về kết quả:

    Một ví dụ về hàm JavaScript đơn giản var result = addValues(10, 20) document.write("Result = " + result);

    Trong ví dụ trên, chúng ta đang chuyển các giá trị 10 và 20 cho hàm addValues. Hàm addValues ​​thêm hai giá trị này và trả về kết quả. Câu lệnh return gán kết quả cho biến kết quả, sau đó biến này được sử dụng để tạo một chuỗi được in trên trang HTML.

    Lệnh gọi hàm JavaScript có thể được thực hiện ở nhiều nơi khác nhau. Ví dụ, không cần thiết phải gán kết quả làm giá trị của một biến. Bạn có thể sử dụng nó trực tiếp làm đối số khi gọi document.write .

    Điều quan trọng cần lưu ý là một hàm chỉ có thể trả về một giá trị:

    Một ví dụ đơn giản về hàm JavaScript addValues(value1, value2) ( return value1 + value2; ) document.write("Result = " + addValues(10, 20)); Các hàm onclick của JavaScript cũng có thể được sử dụng trong các câu lệnh có điều kiện. Ví dụ: Một ví dụ đơn giản về hàm JavaScript function addValues(value1, value2) ( return value1 + value2; ) if (addValues(10, 20) > 20) ( document.write("Result > 20"); ) else ( document.write( "Kết quả< 20"); }

    Nơi đặt khai báo hàm

    Có hai nơi bạn nên đặt các khai báo trả về hàm JavaScript: bên trong một phần của tài liệu HTML hoặc trong tệp .js bên ngoài. Tùy chọn thứ hai được coi là vị trí ưa thích nhất vì nó mang lại sự linh hoạt cao nhất.

    Mục tiêu của việc tạo ra các chức năng là làm cho chúng trở nên tổng quát nhất có thể nhằm tối đa hóa khả năng sử dụng lại.

    Bản dịch của bài viết “Tìm hiểu về hàm JavaScript” được nhóm dự án thân thiện chuẩn bị.

    Tốt xấu

    Hàm là một trong những khối xây dựng cơ bản trong JavaScript. Hàm là một thủ tục JavaScript - một tập hợp các câu lệnh thực hiện một tác vụ hoặc tính toán một giá trị. Để sử dụng một hàm, bạn phải định nghĩa nó ở đâu đó trong phạm vi mà bạn muốn gọi nó.

    Một phương thức là một hàm là một thuộc tính của một đối tượng. Đọc thêm về đối tượng và phương thức trong Làm việc với đối tượng.

    Chức năng gọi

    Việc xác định một hàm không thực thi nó. Việc xác định hàm chỉ đơn giản là đặt tên cho hàm và chỉ định những việc cần làm khi hàm được gọi. Việc gọi hàm thực sự thực hiện các hành động đã chỉ định với các tham số đã chỉ định. Ví dụ: nếu bạn xác định hàm Square , bạn có thể gọi nó như sau:

    Hình vuông(5);

    Câu lệnh trước gọi hàm với đối số là 5. Hàm thực thi các câu lệnh của nó và trả về giá trị 25.

    Các hàm phải nằm trong phạm vi khi chúng được gọi, nhưng phần khai báo hàm có thể được nâng lên (xuất hiện bên dưới lệnh gọi trong mã), như trong ví dụ này:

    Console.log(vuông(5)); /* ... */ hàm bình phương(n) ( return n * n; )

    Phạm vi của một hàm là hàm mà nó được khai báo hoặc toàn bộ chương trình nếu nó được khai báo ở cấp cao nhất.

    Lưu ý: Điều này chỉ hoạt động khi xác định hàm bằng cú pháp trên (tức là hàm funcName()()). Mã bên dưới sẽ không hoạt động. Điều đó có nghĩa là việc hoisting hàm chỉ hoạt động với việc khai báo hàm chứ không hoạt động với biểu thức hàm.

    Console.log(vuông); // hình vuông được nâng lên với giá trị ban đầu không xác định. console.log(vuông(5)); // TypeError: hình vuông không phải là hàm var Square = function(n) ( return n * n; )

    Các đối số của hàm không bị giới hạn ở chuỗi và số. Bạn có thể chuyển toàn bộ đối tượng cho một hàm. Hàm show_props() (được định nghĩa trong ) là một ví dụ về hàm lấy một đối tượng làm đối số.

    Một hàm có thể gọi chính nó. Ví dụ: đây là một hàm tính giai thừa theo cách đệ quy:

    Hàm giai thừa(n) ( if ((n === 0) || (n === 1)) return 1; ngược lại return (n * giai thừa(n - 1)); )

    Khi đó bạn có thể tính giai thừa từ một đến năm như sau:

    Var a, b, c, d, e; a = giai thừa(1); // a nhận giá trị 1 b = giai thừa(2); // b nhận giá trị 2 c = giai thừa(3); // c nhận giá trị 6 d = giai thừa(4); // d nhận giá trị 24 e = giai thừa(5); //e nhận giá trị 120

    Có nhiều cách khác để gọi hàm. Thường có những trường hợp trong đó một hàm cần được gọi động hoặc số lượng đối số của một hàm khác nhau hoặc trong đó ngữ cảnh của lệnh gọi hàm cần được đặt thành một đối tượng cụ thể được xác định trong thời gian chạy. Hóa ra bản thân các hàm là các đối tượng và các đối tượng này lần lượt có các phương thức (xem đối tượng Hàm). Một trong số đó, phương thức apply(), có thể được sử dụng để đạt được mục tiêu này.

    Phạm vi chức năng

    Các biến được xác định bên trong hàm không thể được truy cập từ bất kỳ đâu bên ngoài hàm, vì biến chỉ được xác định trong phạm vi của hàm. Tuy nhiên, một hàm có thể truy cập tất cả các biến và hàm được xác định bên trong phạm vi mà nó được xác định. Nói cách khác, một hàm được xác định trong phạm vi toàn cục có thể truy cập tất cả các biến được xác định trong phạm vi toàn cục. Một hàm được xác định bên trong một hàm khác cũng có thể truy cập vào tất cả các biến được xác định trong hàm cha của nó và bất kỳ biến nào khác mà hàm cha có quyền truy cập.

    // Các biến sau được định nghĩa trong phạm vi toàn cục var num1 = 20, num2 = 3, name = "Chamahk"; // Hàm này được định nghĩa trong phạm vi toàn cục function multiple() ( return num1 * num2; ) multiple(); // Trả về 60 // Ví dụ về hàm lồng nhau function getScore() ( var num1 = 2, num2 = 3; function add() ( return name + " đã ghi điểm " + (num1 + num2); ) return add(); ) getScore (); // Trả về "Chamahk ghi được 5"

    Phạm vi và ngăn xếp hàm Đệ quy

    Một hàm có thể tham chiếu và gọi chính nó. Có ba cách để một hàm tham chiếu đến chính nó:

  • tên của hàm
  • một biến trong phạm vi đề cập đến hàm
  • Ví dụ, hãy xem xét định nghĩa hàm sau:

    Var foo = function bar() ( // các câu lệnh ở đây );

    Trong thân hàm, tất cả những điều sau đây đều tương đương:

  • quán ba()
  • đối số.callee()
  • foo()
  • Hàm tự gọi chính nó được gọi là Hàm đệ quy. Ở một khía cạnh nào đó, đệ quy tương tự như một vòng lặp. Cả hai đều thực thi cùng một mã nhiều lần và cả hai đều yêu cầu một điều kiện (để tránh vòng lặp vô hạn, hay đúng hơn là đệ quy vô hạn trong trường hợp này). Ví dụ: vòng lặp sau:

    Var x = 0; trong khi(x< 10) { // "x < 10" is the loop condition // do stuff x++; }

    có thể được chuyển đổi thành hàm đệ quy và gọi hàm đó:

    Hàm loop(x) ( if (x >= 10) // "x >= 10" là điều kiện thoát (tương đương với "!(x< 10)") return; // do stuff loop(x + 1); // the recursive call } loop(0);

    Tuy nhiên, một số thuật toán không thể là các vòng lặp đơn giản. Ví dụ: việc lấy tất cả các nút của cấu trúc cây (ví dụ: DOM) được thực hiện dễ dàng hơn bằng cách sử dụng đệ quy:

    Hàm walkTree(node) ( if (node ​​​​== null) // return; // làm gì đó với node for (var i = 0; i< node.childNodes.length; i++) { walkTree(node.childNodes[i]); } }

    So với vòng lặp hàm, mỗi lệnh gọi đệ quy tự nó thực hiện nhiều lệnh gọi đệ quy ở đây.

    Có thể chuyển đổi bất kỳ thuật toán đệ quy nào thành thuật toán không đệ quy, nhưng thường logic phức tạp hơn nhiều và làm như vậy đòi hỏi phải sử dụng ngăn xếp. Trong thực tế, bản thân đệ quy sử dụng một ngăn xếp: ngăn xếp hàm.

    Hành vi giống như ngăn xếp có thể được nhìn thấy trong ví dụ sau:

    Hàm foo(i) ( if (i< 0) return; console.log("begin: " + i); foo(i - 1); console.log("end: " + i); } foo(3); // Output: // begin: 3 // begin: 2 // begin: 1 // begin: 0 // end: 0 // end: 1 // end: 2 // end: 3

    Các hàm lồng nhau và các bao đóng

    Bạn có thể lồng một hàm vào trong một hàm. Hàm lồng nhau (bên trong) là riêng tư đối với hàm chứa (bên ngoài) của nó. Nó cũng tạo thành một Khép kín. Bao đóng là một biểu thức (thường là một hàm) có thể có các biến tự do cùng với một môi trường liên kết các biến đó ("đóng" biểu thức).

    Vì hàm lồng nhau là một bao đóng, điều này có nghĩa là hàm lồng nhau có thể "kế thừa" các đối số và biến của hàm chứa nó. Nói cách khác, hàm bên trong chứa phạm vi của hàm bên ngoài.

    • Hàm bên trong chỉ có thể được truy cập từ các câu lệnh ở hàm bên ngoài.
    • Hàm bên trong tạo thành một bao đóng: hàm bên trong có thể sử dụng các đối số và biến của hàm bên ngoài, trong khi hàm bên ngoài không thể sử dụng các đối số và biến của hàm bên trong.

    Ví dụ sau đây cho thấy các hàm lồng nhau:

    Hàm addSquares(a, b) ( hàm bình phương(x) ( return x * x; ) trả về bình phương(a) + bình phương(b); ) a = addSquares(2, 3); // trả về 13 b = addSquares(3, 4); // trả về 25 c = addSquares(4, 5); // trả về 41

    Vì hàm bên trong tạo thành một bao đóng, nên bạn có thể gọi hàm ngoài và chỉ định các đối số cho cả hàm ngoài và hàm bên trong:

    Hàm bên ngoài(x) ( hàm bên trong(y) ( return x + y; ) trả về bên trong; ) fn_inside = bên ngoài(3); // Hãy nghĩ về nó như sau: cho tôi một hàm cộng 3 với bất cứ thứ gì bạn đưa ra // ​​it result = fn_inside(5); // trả về 8 result1 = bên ngoài(3)(5); // trả về 8

    Bảo toàn các biến

    Lưu ý cách x được bảo toàn khi trả về bên trong. Một bao đóng phải bảo toàn các đối số và các biến trong tất cả các phạm vi mà nó tham chiếu. Vì mỗi cuộc gọi cung cấp các đối số tiềm năng khác nhau nên một bao đóng mới sẽ được tạo cho mỗi cuộc gọi ra bên ngoài. Bộ nhớ chỉ có thể được giải phóng khi phần bên trong được trả về không còn truy cập được nữa.

    Điều này không khác với việc lưu trữ các tham chiếu trong các đối tượng khác, nhưng thường ít rõ ràng hơn vì người ta không trực tiếp đặt các tham chiếu và không thể kiểm tra chúng.

    Các hàm lồng nhau

    Các hàm có thể được lồng nhiều lần, tức là hàm (A) chứa hàm (B) chứa hàm (C). Cả hai hàm B và C đều tạo thành các bao đóng ở đây, vì vậy B có thể truy cập A và C có thể truy cập B. Ngoài ra, vì C có thể truy cập B và có thể truy cập A nên C cũng có thể truy cập A. Do đó, các bao đóng có thể chứa nhiều phạm vi; chúng đệ quy chứa phạm vi của các hàm chứa nó. Đây được gọi là chuỗi phạm vi. (Tại sao nó được gọi là "chuỗi" sẽ được giải thích sau.)

    Hãy xem xét ví dụ sau:

    Hàm A(x) ( hàm B(y) ( hàm C(z) ( console.log(x + y + z); ) C(3); ) B(2); ) A(1); // log 6 (1 + 2 + 3)

    Trong ví dụ này, C truy cập B "s y và A" s x . Điều này có thể được thực hiện bởi vì:

  • B tạo thành một bao đóng bao gồm A, tức là B có thể truy cập các đối số và biến của A.
  • C tạo thành một bao đóng bao gồm B.
  • Bởi vì bao đóng của B bao gồm A , bao đóng của C bao gồm A , C có thể truy cập cả B Các đối số và biến của A. Nói cách khác, C dây chuyền phạm vi của B và A theo thứ tự đó.
  • Tuy nhiên, điều ngược lại là không đúng. A không thể truy cập C , bởi vì A không thể truy cập bất kỳ đối số hoặc biến nào của B , mà C là một biến của. Do đó, C chỉ ở chế độ riêng tư đối với B .

    Xung đột tên

    Khi hai đối số hoặc biến trong phạm vi của một bao đóng có cùng tên, thì sẽ có một xung đột tên. Nhiều phạm vi bên trong hơn được ưu tiên, do đó phạm vi trong cùng có mức độ ưu tiên cao nhất, trong khi phạm vi ngoài cùng có mức độ ưu tiên thấp nhất. Đây là chuỗi phạm vi. Phạm vi đầu tiên trong chuỗi là phạm vi trong cùng và phạm vi cuối cùng là phạm vi ngoài cùng. Hãy xem xét những điều sau:

    Hàm bên ngoài() ( var x = 5; hàm bên trong(x) ( return x * 2; ) return bên trong; ) bên ngoài())(10); // trả về 20 thay vì 10

    Xung đột tên xảy ra tại câu lệnh return x và nằm giữa tham số x bên trong và biến x bên ngoài. Chuỗi phạm vi ở đây là ( đối tượng bên trong, bên ngoài, toàn cầu). Do đó, bên trong "s x được ưu tiên hơn bên ngoài "s x và 20 (bên trong "s x) được trả về thay vì 10 (bên ngoài "s x).

    Đóng cửa

    Closure là một trong những tính năng mạnh mẽ nhất của JavaScript. JavaScript cho phép lồng các hàm và cấp cho hàm bên trong toàn quyền truy cập vào tất cả các biến và hàm được xác định bên trong hàm bên ngoài (và tất cả các biến và hàm khác mà hàm bên ngoài có quyền truy cập). Tuy nhiên, hàm ngoài không có quyền truy cập vào các biến và hàm được xác định bên trong hàm bên trong. Điều này cung cấp một kiểu đóng gói cho các biến của hàm bên trong. Ngoài ra, do hàm bên trong có quyền truy cập vào phạm vi của hàm bên ngoài nên các biến và hàm được xác định trong hàm bên ngoài sẽ tồn tại lâu hơn thời gian thực thi hàm bên ngoài, nếu hàm bên trong quản lý để tồn tại ngoài vòng đời của hàm bên ngoài. chức năng. Một bao đóng được tạo ra khi hàm bên trong bằng cách nào đó được cung cấp cho bất kỳ phạm vi nào bên ngoài hàm bên ngoài.

    Var pet = function(name) ( // Hàm ngoài định nghĩa một biến tên là "name" var getName = function() ( return name; // Hàm bên trong có quyền truy cập vào biến "name" của // hàm bên ngoài ) return getName; // Trả về hàm bên trong, từ đó hiển thị nó ra phạm vi bên ngoài ) myPet = pet("Vivie"); myPet(); // Trả về "Vivie"

    Nó có thể phức tạp hơn nhiều so với đoạn mã trên. Một đối tượng chứa các phương thức để thao tác các biến bên trong của hàm bên ngoài có thể được trả về.

    Var createPet = function(name) ( var sex; return ( setName: function(newName) ( name = newName; ), getName: function() ( return name; ), getSex: function() ( return sex; ), setSex: function(newSex) ( if(typeof newSex === "string" && (newSex.toLowerCase() === "nam" || newSex.toLowerCase() === "nữ")) ( sex = newSex; ) ) ) ) var pet = createPet("Vivie"); pet.getName(); // Vivie pet.setName("Oliver"); pet.setSex("nam"); pet.getSex(); // pet.getName(); //Oliver

    Trong đoạn mã trên, các hàm bên trong có thể truy cập được biến tên của hàm bên ngoài và không có cách nào khác để truy cập các biến bên trong ngoại trừ thông qua các hàm bên trong. Các biến bên trong của hàm bên trong đóng vai trò là kho lưu trữ an toàn cho các đối số và biến bên ngoài. Chúng chứa dữ liệu "liên tục" và "được đóng gói" để các hàm bên trong hoạt động. Các hàm thậm chí không cần phải được gán cho một biến hoặc có tên.

    Var getCode = (function() ( var apiCode = "0]Eal(eh&2"; // Một code chúng ta không muốn người ngoài có thể sửa đổi... return function() ( return apiCode; ); ))() ; getCode(); // Trả về apiCode

    Tuy nhiên, có một số cạm bẫy cần chú ý khi sử dụng các bao đóng. Nếu một hàm kèm theo định nghĩa một biến có cùng tên với tên của một biến trong phạm vi bên ngoài thì không có cách nào để tham chiếu lại biến đó trong phạm vi bên ngoài.

    Var createPet = function(name) ( // Hàm ngoài định nghĩa một biến tên là "name". return ( setName: function(name) ( // Hàm kèm theo cũng định nghĩa một biến tên là "name". name = name; // Làm cách nào để chúng ta truy cập vào "tên" được xác định bởi hàm bên ngoài?

    Sử dụng đối tượng đối số

    Các đối số của hàm được duy trì trong một đối tượng giống như mảng. Trong một hàm, bạn có thể xử lý các đối số được truyền cho nó như sau:

    Đối số[i]

    trong đó i là số thứ tự của đối số, bắt đầu từ số 0. Vì vậy, đối số đầu tiên được truyền cho hàm sẽ là đối số. Tổng số đối số được biểu thị bằng đối số.length.

    Bằng cách sử dụng đối tượng đối số, bạn có thể gọi một hàm có nhiều đối số hơn mức được khai báo chính thức để chấp nhận. Điều này thường hữu ích nếu bạn không biết trước có bao nhiêu đối số sẽ được truyền cho hàm. Bạn có thể sử dụng đối số.length để xác định số lượng đối số thực sự được truyền cho hàm, sau đó truy cập từng đối số bằng cách sử dụng đối tượng đối số.

    Ví dụ, hãy xem xét một hàm nối nhiều chuỗi. Đối số chính thức duy nhất cho hàm là một chuỗi xác định các ký tự phân tách các mục cần nối. Hàm được định nghĩa như sau:

    Hàm myConcat(separator) ( var result = ""; // khởi tạo danh sách var i; // lặp qua các đối số cho (i = 1; i< arguments.length; i++) { result += arguments[i] + separator; } return result; }

    Bạn có thể chuyển bất kỳ số lượng đối số nào cho hàm này và nó nối từng đối số thành một chuỗi "danh sách":

    // trả về "đỏ, cam, xanh," myConcat(", ", "đỏ", "cam", "xanh"); // trả về "voi; hươu cao cổ; sư tử; báo gêpa; " myConcat("; ", "voi", "hươu cao cổ", "sư tử", "báo gêpa"); // trả về "sage. húng quế. oregano. pepper. parsley. " myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

    Lưu ý: Biến đối số là "giống mảng", nhưng không phải là mảng. Nó giống mảng ở chỗ nó có chỉ mục được đánh số và thuộc tính độ dài. Tuy nhiên, nó không sở hữu tất cả các phương pháp thao tác mảng.

    Hai yếu tố ảnh hưởng đến việc giới thiệu các hàm mũi tên: các hàm ngắn hơn và không ràng buộc về điều này.

    Hàm ngắn hơn

    Trong một số mẫu chức năng, các chức năng ngắn hơn được hoan nghênh. So sánh:

    Var a = ["Hydro", "Heli", "Lithium", "Beryllium" ]; var a2 = a.map((các) hàm ( return s.length; )); console.log(a2); // logs var a3 = a.map(s => s.length); console.log(a3); // nhật ký

    Không tách biệt điều này

    Cho đến các hàm mũi tên, mọi hàm mới đều xác định giá trị riêng của nó (một đối tượng mới trong trường hợp hàm tạo, không xác định trong lệnh gọi hàm, đối tượng cơ sở nếu hàm được gọi là "phương thức đối tượng", v.v.). Điều này đã được chứng minh là kém lý tưởng với phong cách lập trình hướng đối tượng.

    Hàm Person() ( // Hàm tạo Person() định nghĩa `this` là chính nó. this.age = 0; setInterval(function GrowingUp() ( // Ở chế độ không hạn chế, hàm GrowUp() định nghĩa `this` // là đối tượng toàn cục, khác với `this` // được định nghĩa bởi hàm tạo Person(). this.age++), 1000);

    Trong ECMAScript 3/5, sự cố này đã được khắc phục bằng cách gán giá trị trong biến này cho một biến có thể đóng lại.

    Function Person() ( var self = this; // Một số chọn `that` thay vì `self`. // Chọn một và nhất quán. self.age = 0; setInterval(function GrowingUp() ( // Lệnh gọi lại đề cập đến biến `self` trong đó // giá trị là đối tượng được mong đợi. self.age++ ), 1000);