11/08/2018, 20:34

Gọi hàm trong javascript

Link gốc bài viết tại đây Bài này mình tách ra từ bài viết trước Trong JS, dấu () để gọi thực thi hàm. Function expression là function được khởi tạo bằng phép gán = ; function có thể có tên hoặc không có tên. VD: var f = function() { console.log(5); } // hoặc var f = function ...

Link gốc bài viết tại đây
Bài này mình tách ra từ bài viết trước

Trong JS, dấu () để gọi thực thi hàm.

Function expression là function được khởi tạo bằng phép gán = ; function có thể có tên hoặc không có tên.

VD:

var f = function() {
   console.log(5);
}

// hoặc
var f = function test(){
   console.log(5);
}

Function expression không được hoisting(khác với function declaration giải thích phía dưới)

console.log(a); // Lỗi vì a chưa được khai báo
console.log(f); // undefined vì f đã được khai báo phía dưới nhưng chưa được hoisting
f(); // Lỗi vì f chưa được hoisting nên không biết f đang là kiểu function

var f = function(){
  console.log(5);
}

Function declaration là hàm được khai báo như thông thường, hàm này có tên
Ví dụ:

function f(){
   console.log(5);
}

Khác với function expression, function declaration được hoisting:

console.log(f); // trả về function f thay vì undefined vì nó đã được hoisting
f(); // 5

function f(){
   console.log(5);
}

Function Invocation xảy ra khi gọi một hàm nào đó bằng cách sử dụng dấu (). Cách gọi thực thi hàm như thế được gọi là Function Invocation Pattern.

Ví dụ:

var add = function(num1, num2) {
    console.log(num1 + num2);
}

add(2, 3); // 5

Xét ví dụ:

var createCallBack = function() { //First function
        console.log("Here is in first function");

        return function() { //Second function
            console.log("Here is in second function");

            return function() { //Third function
                console.log("Here is in third function");
            }
        }
    }

createCallBack;// caller 1
createCallBack();// caller 2
window.onload = createCallBack; // caller 3
window.onload = createCallBack(); // caller 4
window.onload = createCallBack()();// caller 5

Nhớ là gọi caller 1 tới caller 5 từng lượt một chứ không phải gọi cùng lúc như trên nhé
Theo bạn tương ứng với mỗi câu lệnh caller 1 -> caller 5 thì kết quả in ra console là gì?

Nếu bạn thử kiểm tra từng câu lệnh trên bằng trình duyệt thì hãy gọi từng câu lệnh một chứ đừng gọi cả 5 câu lệnh cùng lúc như trên vì js là xử lý bất đồng bộ (assynchronus) nên sẽ không in ra đúng thứ tự cho bạn đâu.

  • caller 1 sẽ chẳng in ra cho bạn cái gì cả, vì hàm createCallBack đã được gọi thực thi đâu.

  • caller 2 sẽ in ra cho bạn "Here is in first function"

  • caller 3 sẽ in ra cho bạn "Here is in first function". Nhiều người sẽ thắc mắc vì sao caller 1caller 3 trông giống nhau mà kết qủa lại khác nhau?
    caller 3 là hàm createCallBack được truyền vào dưới dạng callback của event (sự kiện trên trình duyệt) nên khi event đó (ở vd trên là window.onload) được trigger thì hàm callback truyền vào sẽ tự động được thực thi.

Mọi hàm callback được truyền vào event thì sẽ được tự động thực thi ngay khi event đó trigger.

  • caller 4 sẽ in ra 2 dòng "Here is in first function" và "Here is in second function". Như bạn thấy caller 4 chỉ là thêm dấu () vào sau caller 3, nghĩa là nó thực thi caller 3 xong rồi thực thi tiếp hàm mà caller 3 trả về (vd trên là second function)

  • caller 5 giải thích tương tự caller 4, kết qủa in ra là "Here is in first function", "Here is in second function" và "Here is in third function".

Thế còn thế này thì sao?:

var createCallBack = function() { //First function
        console.log("first function");

        return function() { //Second function
            console.log("second function");

            return function() { //Third function
                console.log("third function");
            }
        }
    }

window.onload = function() { createCallBack; }; // caller 6
window.onload = function() { createCallBack(); }; // caller 7

Nhắc lại là mọi hàm callback được truyền vào event thì sẽ được tự động thực thi ngay khi event đó trigger.

Nghĩa là 2 function caller 6caller 7 trên đều được thực thi ngay khi window load, thực thi ngay ở đây nghĩa là thực thi nội dung trong dấu {} của 2 function truyền vào cho event window.onload.

  • caller 6 sẽ không in ra cái gì cả vì hàm createCallBack không được gọi thực thi.

  • caller 7 sẽ in ra "first function" vì hàm createCallBack được gọi thực thi bằng cách thêm dấu ()

Constructor Invocation Pattern là cách gọi hàm bằng cách thêm từ khóa new phía trước.

Ví dụ:

var createCallBack = function() { //First function
        console.log("first function");

        return new function() { //Second function
            console.log("second function");

            return function() { //Third function
                console.log("third function");
            }
        }
    }

window.onload = createCallBack;

Ví dụ trên giống với ví dụ ở phía trên với lời gọi caller 3. Nhưng lời gọi này ở phía trên chỉ in ra "first function" còn ở ví dụ này là "first function" và "second function". Vì sao?

Câu trả lời nằm ở từ khóa new đặt trước second function.
Từ khóa new chứng tỏ second function đã được gọi thực thi ngay (Constructor Invocation Pattern).

Ví dụ trên có thể được viết lại như sau:

var createCallBack = function() { //First function
        console.log("first function");

        var secondFunction = function() { //Second function
            console.log("second function");

            return function() { //Third function
                console.log("third function");
            }
        }

        return secondFunction();
    }

window.onload = createCallBack;

Hàm được gọi bằng kỹ thuật Constructor Invocation Pattern (dùng từ khóa new) sẽ trả về:

  • Nếu hàm có return các kiểu đơn như number, string, boolean, null hoặc undefined thì giá trị trả về sẽ bị bỏ đi và trả về this (là object được tạo ra từ từ khóa new).

  • Nếu hàm có return là một object (là mọi thứ trừ các kiểu đơn), thì object này sẽ được return thay vì this.

Với ví dụ trên thì second function sẽ return third function.

Ví dụ:

<button onclick=myFunction()>Click me</button>
 

<script>
function myFunction() {
  console.log(5);
}
</script>

Khác với giải thích ở trên, khi truyền vào callback cho inline event handler (onclick) thì thứ được truyền vào phải là một expression (câu lệnh thực thi) hay một lời gọi thực thi hàm (như trên) chứ không phải là một khai báo hàm. Tuy có dấu () được đặt vào ngay sau tên hàm nhưng ở thời điểm biên dịch, hàm vẫn chưa được thực thi. Chỉ khi nào event onclick được trigger thì hàm mới được thực thi.

Nó giống như bạn truyền vào onclick một đoạn mã textABC, khi event được trigger thì nó sẽ thực thi câu lệnh eval(textABC) (Xem thêm về eval tại đây)

Link tham khảo:
http://doctrina.org/Javascript-Function-Invocation-Patterns.html#fi
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function

0