Tôi đã đào sâu để hiểu hành vi cụ thể này và tôi nghĩ rằng tôi đã tìm thấy một lời giải thích tốt.
Trước khi tìm hiểu lý do tại sao bạn không thể đặt bí danh document.getElementById
, tôi sẽ cố gắng giải thích cách hoạt động của các hàm / đối tượng JavaScript.
Bất cứ khi nào bạn gọi một hàm JavaScript, trình thông dịch JavaScript sẽ xác định một phạm vi và chuyển nó cho hàm.
Hãy xem xét chức năng sau:
function sum(a, b)
{
return a + b;
}
sum(10, 20);
Hàm này được khai báo trong phạm vi Window và khi bạn gọi nó, giá trị this
bên trong hàm sum sẽ là Window
đối tượng toàn cục .
Đối với hàm 'sum', không quan trọng giá trị của 'this' vì nó không sử dụng nó.
Hãy xem xét chức năng sau:
function Person(birthDate)
{
this.birthDate = birthDate;
this.getAge = function() { return new Date().getFullYear() - this.birthDate.getFullYear(); };
}
var dave = new Person(new Date(1909, 1, 1));
dave.getAge();
Khi bạn gọi hàm dave.getAge, trình thông dịch JavaScript thấy rằng bạn đang gọi hàm getAge trên dave
đối tượng, vì vậy nó đặt this
thành dave
và gọi getAge
hàm. getAge()
chính xác sẽ trở lại 100
.
Bạn có thể biết rằng trong JavaScript, bạn có thể chỉ định phạm vi bằng apply
phương pháp này. Hãy thử điều đó.
var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));
dave.getAge.apply(bob);
Trong dòng trên, thay vì để JavaScript quyết định phạm vi, bạn đang chuyển phạm vi theo cách thủ công dưới dạng bob
đối tượng. getAge
bây giờ sẽ trở lại 200
ngay cả khi bạn 'nghĩ' bạn gọi getAge
trên dave
đối tượng.
Điểm của tất cả những điều trên là gì? Các hàm được gắn 'lỏng lẻo' với các đối tượng JavaScript của bạn. Vd bạn có thể làm
var dave = new Person(new Date(1909, 1, 1));
var bob = new Person(new Date(1809, 1, 1));
bob.getAge = function() { return -1; };
bob.getAge();
dave.getAge();
Hãy thực hiện bước tiếp theo.
var dave = new Person(new Date(1909, 1, 1));
var ageMethod = dave.getAge;
dave.getAge();
ageMethod();
ageMethod
thực thi ném một lỗi! Chuyện gì đã xảy ra?
Nếu bạn đọc kỹ những điểm trên của tôi, bạn sẽ lưu ý rằng dave.getAge
phương thức được gọi dave
là this
đối tượng trong khi JavaScript không thể xác định 'phạm vi' để ageMethod
thực thi. Vì vậy, nó đã chuyển toàn cầu 'Cửa sổ' là 'cái này'. Bây giờ vì window
không có thuộc birthDate
tính, ageMethod
việc thực thi sẽ không thành công.
Làm thế nào để khắc phục điều này? Đơn giản,
ageMethod.apply(dave);
Tất cả những điều trên có hợp lý không? Nếu có, thì bạn sẽ có thể giải thích tại sao bạn không thể đặt bí danh document.getElementById
:
var $ = document.getElementById;
$('someElement');
$
được gọi với window
theo this
và nếu getElementById
thực hiện dự kiến this
là document
, nó sẽ thất bại.
Một lần nữa để khắc phục điều này, bạn có thể làm
$.apply(document, ['someElement']);
Vậy tại sao nó hoạt động trong Internet Explorer?
Tôi không biết việc triển khai nội bộ của getElementById
trong IE, nhưng một nhận xét trong nguồn jQuery ( inArray
triển khai phương thức) nói rằng trong IE , window == document
. Nếu đúng như vậy, thì răng cưa document.getElementById
sẽ hoạt động trong IE.
Để minh họa thêm điều này, tôi đã tạo ra một ví dụ phức tạp. Hãy xem Person
chức năng bên dưới.
function Person(birthDate)
{
var self = this;
this.birthDate = birthDate;
this.getAge = function()
{
if(this.constructor == Person)
return new Date().getFullYear() - this.birthDate.getFullYear();
else
return -1;
};
this.getAgeSmarter = function()
{
return self.getAge();
};
this.getAgeSmartest = function()
{
var scope = this.constructor == Person ? this : self;
return scope.getAge();
};
}
Đối với Person
hàm trên, đây là cách các getAge
phương thức khác nhau sẽ hoạt động.
Hãy tạo hai đối tượng bằng Person
hàm.
var yogi = new Person(new Date(1909, 1,1));
var anotherYogi = new Person(new Date(1809, 1, 1));
console.log(yogi.getAge());
Về phía trước, phương thức getAge nhận yogi
đối tượng là this
và xuất ra 100
.
var ageAlias = yogi.getAge;
console.log(ageAlias());
Công cụ hỗ trợ JavaScript đặt window
đối tượng là đối tượng this
và getAge
phương thức của chúng tôi sẽ trả về -1
.
console.log(ageAlias.apply(yogi));
Nếu chúng tôi đặt phạm vi chính xác, bạn có thể sử dụng ageAlias
phương pháp.
console.log(ageAlias.apply(anotherYogi));
Nếu chúng ta truyền vào một số đối tượng người khác, nó vẫn sẽ tính tuổi một cách chính xác.
var ageSmarterAlias = yogi.getAgeSmarter;
console.log(ageSmarterAlias());
Các ageSmarter
chức năng chụp bản gốc this
đối tượng vì vậy bây giờ bạn không cần phải lo lắng về cung cấp phạm vi chính xác.
console.log(ageSmarterAlias.apply(anotherYogi));
Vấn đề ageSmarter
là bạn không bao giờ có thể đặt phạm vi cho một số đối tượng khác.
var ageSmartestAlias = yogi.getAgeSmartest;
console.log(ageSmartestAlias());
console.log(ageSmartestAlias.apply(document));
Các ageSmartest
chức năng sẽ sử dụng phạm vi ban đầu nếu một phạm vi không hợp lệ được cung cấp.
console.log(ageSmartestAlias.apply(anotherYogi));
Bạn vẫn có thể chuyển một Person
đối tượng khác tới getAgeSmartest
. :)