Chức năng gọi
Các chức năng chỉ là một loại đối tượng.
Tất cả các đối tượng Hàm có lệnh gọi và áp dụng các phương thức thực thi đối tượng Hàm mà chúng được gọi.
Khi được gọi, đối số đầu tiên cho các phương thức này chỉ định đối tượng sẽ được tham chiếu bởi this
từ khóa trong khi thực thi Hàm - nếu nó null
hoặc undefined
, đối tượng toàn cục window
, được sử dụng cho this
.
Do đó, gọi một Hàm ...
whereAmI = "window";
function foo()
{
return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}
... với dấu ngoặc đơn - foo()
- tương đương với foo.call(undefined)
hoặc foo.apply(undefined)
, có hiệu quả tương tự như foo.call(window)
hoặc foo.apply(window)
.
>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"
Các đối số bổ sung call
được truyền dưới dạng đối số cho lệnh gọi hàm, trong khi một đối số bổ sung duy nhất apply
có thể chỉ định các đối số cho lệnh gọi hàm là một đối tượng giống như mảng.
Như vậy, foo(1, 2, 3)
tương đương với foo.call(null, 1, 2, 3)
hoặc foo.apply(null, [1, 2, 3])
.
>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"
Nếu một chức năng là một thuộc tính của một đối tượng ...
var obj =
{
whereAmI: "obj",
foo: foo
};
... truy cập một tham chiếu đến Hàm thông qua đối tượng và gọi nó bằng dấu ngoặc đơn - obj.foo()
- tương đương với foo.call(obj)
hoặc foo.apply(obj)
.
Tuy nhiên, các hàm được giữ làm thuộc tính của các đối tượng không bị "ràng buộc" với các đối tượng đó. Như bạn có thể thấy trong định nghĩa obj
ở trên, vì Hàm chỉ là một loại Đối tượng, chúng có thể được tham chiếu (và do đó có thể được chuyển qua tham chiếu đến lệnh gọi Hàm hoặc được trả về bởi tham chiếu từ lệnh gọi Hàm). Khi tham chiếu đến Hàm được truyền, không có thông tin bổ sung nào về nơi nó được truyền từ đó, đó là lý do tại sao điều sau đây xảy ra:
>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"
Cuộc gọi đến tham chiếu Chức năng của chúng tôi baz
, không cung cấp bất kỳ ngữ cảnh nào cho cuộc gọi, vì vậy nó thực sự giống như baz.call(undefined)
vậy, vì vậy this
kết thúc việc tham chiếu window
. Nếu chúng ta muốn baz
biết rằng nó thuộc về obj
, chúng ta cần bằng cách nào đó cung cấp thông tin đó khi baz
được gọi, đó là nơi tranh luận đầu tiên call
hoặc apply
và đóng cửa phát huy tác dụng.
Chuỗi phạm vi
function bind(func, context)
{
return function()
{
func.apply(context, arguments);
};
}
Khi một Hàm được thực thi, nó tạo ra một phạm vi mới và có một tham chiếu đến bất kỳ phạm vi kèm theo nào. Khi hàm ẩn danh được tạo trong ví dụ trên, nó có tham chiếu đến phạm vi mà nó được tạo, đó là bind
phạm vi. Điều này được gọi là "đóng cửa."
[global scope (window)] - whereAmI, foo, obj, baz
|
[bind scope] - func, context
|
[anonymous scope]
Khi bạn cố gắng truy cập vào một biến, "chuỗi phạm vi" này sẽ được tìm thấy một biến có tên đã cho - nếu phạm vi hiện tại không chứa biến đó, bạn hãy xem phạm vi tiếp theo trong chuỗi, và cứ thế cho đến khi bạn đạt được phạm vi toàn cầu. Khi hàm ẩn danh được trả về và bind
kết thúc thực thi, hàm ẩn danh vẫn có tham chiếu đến bind
phạm vi, vì vậy bind
phạm vi không "biến mất".
Với tất cả những điều trên, giờ đây bạn có thể hiểu phạm vi hoạt động như thế nào trong ví dụ sau và tại sao kỹ thuật chuyển một hàm xung quanh "giới hạn trước" với một giá trị cụ thể của this
nó sẽ có khi nó được gọi là hoạt động:
>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"
var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};