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ị thisbê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 thisthành davevà gọi getAgehà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 applyphươ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. getAgebây giờ sẽ trở lại 200ngay cả khi bạn 'nghĩ' bạn gọi getAgetrê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();
ageMethodthự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.getAgephương thức được gọi davelà thisđối tượng trong khi JavaScript không thể xác định 'phạm vi' để ageMethodthực thi. Vì vậy, nó đã chuyển toàn cầu 'Cửa sổ' là 'cái này'. Bây giờ vì windowkhông có thuộc birthDatetính, ageMethodviệ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 windowtheo thisvà nếu getElementByIdthực hiện dự kiến thislà 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 getElementByIdtrong IE, nhưng một nhận xét trong nguồn jQuery ( inArraytriể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.getElementByIdsẽ 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 Personchứ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 Personhàm trên, đây là cách các getAgephương thức khác nhau sẽ hoạt động.
Hãy tạo hai đối tượng bằng Personhà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à thisvà 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 thisvà getAgephươ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 ageAliasphươ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 ageSmarterchứ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 đề ageSmarterlà 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 ageSmartestchứ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. :)