2017-12-10 173 views
6

Tôi có một hành vi JavaScript lạ mà tôi không thể giải thích.innerHTML và  

xem xét mã này:

var el = document.createElement('div') 
var s = String.fromCharCode(160) 

el.innerHTML = s 

console.log(s)   // prints space 
console.log(el.innerHTML) // prints " " 

Bây giờ tôi biết rằng   là không gian không bị phá hủy, nhưng về mặt kỹ thuật, tôi chỉ cần thực hiện một bài tập của một biến khác, cả hai đều là chuỗi. Giá trị khác nhau như thế nào?

Có phải là innerHTML loại chuỗi đặc biệt hay gì đó không?

Tính năng này hoạt động như thế nào? Cơ chế dịch này là gì?

Trả lời

9

về mặt kỹ thuật, tôi chỉ gán một biến cho một biến khác, cả hai đều là chuỗi. Làm thế nào đến các giá trị khác nhau?

Vì có, innerHTML là đặc biệt. Nó không chỉ là một thuộc tính giá trị đơn giản, nó là một tài sản accessor. Khi bạn gán cho nó, bạn đang gọi một hàm nằm trong phạm vi phân tích cú pháp HTML mà bạn cung cấp và tạo các nút và các phần tử DOM cần thiết. Khi bạn đọc giá trị của nó, bạn đang gọi một hàm khác nằm trong phạm vi bao quát qua cây DOM bắt đầu từ phần tử mà bạn truy cập nó và xây dựng một chuỗi HTML biểu diễn cây DOM đó.

Vì vậy, trong khi bạn gán ký tự U + 00A0 cho nó, nó đã được chuyển thành nút văn bản DOM; và khi bạn đọc từ đó, nút văn bản DOM được hiển thị dưới dạng chuẩn (theo quy tắc của trình duyệt đó) Chuỗi HTML:  .

Bạn có thể thấy rằng innerHTML không chỉ là một tài sản giá trị đơn giản bằng cách sử dụng DOM để thao tác các phần tử:

var target = document.getElementById("target"); 
 
target.innerHTML = "\u00A0"; 
 
console.log(target.innerHTML); // " " 
 
target.appendChild(
 
    document.createElement("span") 
 
); 
 
console.log(target.innerHTML); // "&nbsp;<span></span>"
<div id="target"></div>

Lưu ý rằng nó có thể để xác định tính chất accessor trong đối tượng JavaScript cũng; nó không chỉ là DOM có thể có họ:

var o = { 
 
    get foo() { 
 
    console.log("foo getter called"); 
 
    return this._foo; 
 
    }, 
 
    set foo(value) { 
 
    console.log("foo setter called"); 
 
    this._foo = value; 
 
    } 
 
}; 
 
console.log("foo: " + o.foo); 
 
o.foo = 42; 
 
console.log("foo: " + o.foo);

Thực tế là innerHTML là một tài sản với một getter và setter là lý do tại sao mã như thế này là thực hành kém:

for (var n = 0; n < something.length; ++n) { 
    element.innerHTML += ", " + something[n]; 
} 

Đó là liên tục xây dựng và sau đó phá hủy và xây dựng lại một cây DOM và thực hiện một của các cuộc gọi chức năng ẩn. Thay vào đó, bạn sẽ tạo chuỗi, rồi gán nó.

+0

Cảm ơn, điều này có ý nghĩa rất nhiều, Vì vậy, nó không phải là một giá trị đơn giản thích hợp, nó là loại tài sản phong cách C# với logic đằng sau nó? Trong trường hợp này, thật lạ khi 'el.innerHTML .__ proto__' vẫn nói đây là _String_ –

+0

@YossiVainshtein: Có, giống như thuộc tính C#. Không, không lạ khi 'el.innerHTML .__ proto__' là' String'. :-) Bạn đang truy cập '__proto__' trên giá trị trả về ** của hàm getter (là một chuỗi), không phải là hàm getter. (Giống như 'x = el.innerHTML; x .__ proto __;'). Thay vào đó, bạn muốn xem kết quả của ['Object.getOwnPropertyDescriptor (el," innerHTML ")'] (https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptor), nhưng vì nó thuộc tính DOM, trình duyệt không phải hỗ trợ thao tác đó trên đó. (Nhưng nếu bạn muốn nhìn thấy người truy cập của đối tượng JS ...) –

3

innerHTML không phải là một tài sản đơn giản, nó là một accessor tài sản với gettersetter. Có nghĩa là các chức năng dưới mui xe hoạt động. innerHTML serializes đầu vào đã cho vào HTML. Nó phân tích cú pháp giá trị đã cho, tạo ra các phần tử và các nút, sau đó gắn các giá trị đó vào phần tử.

Nó được định nghĩa như thế này

const DOMElement = { 
 
    value: null, 
 
    set innerHTML(html) { 
 
    console.log(`Setting html: ${html}`); 
 
    // Translate into elements and nodes, then append to the element 
 
    this.value = html; 
 
    }, 
 
    get innerHTML() { 
 
    console.log(`Getting html:`); 
 
    // Translate into string, then return it 
 
    return this.value; 
 
    } 
 
}; 
 

 
DOMElement.innerHTML = '<p>Hello</p>'; 
 
console.log(DOMElement.innerHTML);

Nếu bạn muốn in các nhân vật đưa ra mà không serializing, bạn có thể sử dụng textContent tài sản.

+0

để nó là một Chuỗi đặc biệt, không phải là một thuộc tính thông thường của một đối tượng JavaSciprt thông thường? –

+0

Có, đây không phải là tài sản thông thường. Dưới mui xe nó dịch giá trị nhất định thành kiểu HTML - tạo các nút hoặc các thành phần –

+0

@YossiVainshtein: Điều đó phụ thuộc vào ý bạn là "thông thường". Nó không phải là một thuộc tính * value *, nó là một thuộc tính có các trình truy cập (trong trường hợp này, được định nghĩa bởi đối tượng DOM, nhưng bạn cũng có thể làm điều đó với một đối tượng JavaScript). –