innerHTML和 是什么?

9

我遇到了一个奇怪的 JavaScript 行为,无法解释。

请考虑以下代码:

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

el.innerHTML = s

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

现在我知道 是不间断空格,但从技术上讲,我只是将一个变量分配给另一个变量,两个变量都是字符串。

innerHTML是一种特殊的字符串吗?

它是如何工作的?什么机制可以将其转换?

2个回答

12
技术上来说,我只是将一个变量赋值给另一个变量,两个变量都是字符串。为什么它们的值不同?
是因为innerHTML是特殊的。它不仅仅是一个简单的值属性,它是一个访问器属性。当你对它进行赋值时,你正在调用一个函数,在底层解析你给它的HTML并创建必要的DOM节点和元素。当你读取它的值时,你正在调用另一个函数,在从访问它的元素开始递归通过DOM树,并构建表示该DOM树的HTML字符串。
因此,虽然你将字符U+00A0分配给它,但那被转换为DOM文本节点;当你从它那里读取时,那个DOM文本节点被呈现为一个规范的(根据那个浏览器的规则)HTML字符串: 
你可以通过使用DOM来操作元素,看到innerHTML不仅仅是一个简单的值属性:

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>

请注意,JavaScript对象中也可以定义访问器属性;这不仅限于DOM:

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);

innerHTML是一个带有getter和setter的属性,这就是为什么像这样的代码是不好的实践:

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

这会不断地构建、销毁和重建 DOM 树,并进行大量隐藏的函数调用。相反,您可以先构建字符串,然后再赋值。


谢谢,这很有道理。 所以它不是一个简单的值属性,而是带有逻辑的 C# 风格属性? 在这种情况下,奇怪的是 el.innerHTML.__proto__ 仍然显示这是 _String_。 - Yossi Vainshtein
@YossiVainshtein:是的,就像 C# 的属性一样。并不奇怪 el.innerHTML.__proto__String。 :-) 你正在访问 getter 函数的返回值(它是一个字符串)上的 __proto__,而不是 getter 函数本身(就像 x = el.innerHTML; x.__proto__;)。 相反,您需要查看 Object.getOwnPropertyDescriptor(el, "innerHTML") 的结果,但由于它是 DOM 属性,因此浏览器不必支持它上面的操作。(但如果您想要查看 JS 对象的访问器...) - T.J. Crowder

4

innerHTML 不是一个简单的属性,它是一个带有 gettersetter 的访问器属性。这意味着在底层使用函数进行操作。 innerHTML 将给定输入序列化为 HTML。它解析给定的值,创建元素和节点,然后将它们附加到元素中。

定义如下:

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);

如果您想在不序列化的情况下打印给定的字符,可以使用textContent属性。


那么它是一个特殊的字符串,不是普通的JavaScript对象的常规属性? - Yossi Vainshtein
是的,这不是一个常规的属性。在内部,它会将给定的值转换为 HTML 样式 - 创建节点或元素。 - Suren Srapyan
这取决于你所说的“定期”是什么意思。它不是一个属性,而是一个带有访问器的属性(在这种情况下,由DOM对象定义,但你也可以用JavaScript对象实现)。 - T.J. Crowder
@T.J.Crowder 使用getter和setter,而不仅仅是一个简单的属性。 - Suren Srapyan
@SurenSrapyan:这就是我说的:带有访问器的属性。 - T.J. Crowder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接