◆ innerHTML で取り出すと属性値の部分の <> はエスケープが解除される
  ◆ & と " はそのまま
◆ 属性として取り出したり dataset を使うとエスケープ解除した状態で取得できる
◆ IE だと "" の中に " があれば '' 外側をに置き換える

エスケープする関数
function htmlEsc(h){
const map = {"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;"}
const pattern = Object.keys(map).join("|")
return h.replace(new RegExp(pattern, "g") , function(e){return map[e]})
}

HTML の属性にエスケープが必要な文字を入れます

const text = '<script>alert(true && "abc")</script>'
const esctext = htmlEsc(text)
document.body.innerHTML = '<div data-text="###"></div>'.replace(/###/, esctext)

console.log("---escape---")
console.log(esctext)
console.log("---innerHTML---")
console.log(document.body.innerHTML)
console.log("---dataset.text---")
console.log(document.querySelector("div").dataset.text)
console.log("---attribute data-text---")
console.log(document.querySelector("div").getAttribute("data-text"))
---escape---
&lt;script&gt;alert(true &amp;&amp; &quot;abc&quot;)&lt;/script&gt;
---innerHTML---
<div data-text="<script>alert(true &amp;&amp; &quot;abc&quot;)</script>"></div>
---dataset.text---
<script>alert(true && "abc")</script>
---attribute data-text---
<script>alert(true && "abc")</script>

<>&" をエスケープしてるのに innerHTML で取得すると <> はもとに戻ってます
& と " はエスケープされたままです
dataset.text や getAttriute("data-text") で取得すると全部がエスケープ解除されています

属性の "" の内側なので <> になって戻っていても innerHTML をそのまま別の要素の innerHTML に設定しても大丈夫です
属性を取り出して使う場合はエスケープが解除されてるので 使う場所によっては再度エスケープが必要です

dataset で設定する場合は自動でエスケープされるので エスケープした状態でセットすると innerHTML では 2 重エスケープ状態になります
const div = document.createElement("div")
document.body.appendChild(div)
div.dataset.text = text
div.dataset.esctext = esctext

console.log(div.dataset.text)
console.log(div.dataset.esctext)
console.log(div.outerHTML)
<script>alert(true && "abc")</script>
&lt;script&gt;alert(true &amp;&amp; &quot;abc&quot;)&lt;/script&gt;
<div data-text="<script>alert(true &amp;&amp; &quot;abc&quot;)</script>" data-esctext="&amp;lt;script&amp;gt;alert(true &amp;amp;&amp;amp; &amp;quot;abc&amp;quot;)&amp;lt;/script&amp;gt;"></div>

IE

ところで IE は

---escape---
&lt;script&gt;alert(true &amp;&amp; &quot;abc&quot;)&lt;/script&gt;
---innerHTML---
<div data-text='<script>alert(true &amp;&amp; "abc")</script>'></div>
---dataset.text---
<script>alert(true && "abc")</script>
---attribute data-text---
<script>alert(true && "abc")</script>
<script>alert(true && "abc")</script>
&lt;script&gt;alert(true &amp;&amp; &quot;abc&quot;)&lt;/script&gt;
<div data-text='<script>alert(true &amp;&amp; "abc")</script>' data-esctext="&amp;lt;script&amp;gt;alert(true &amp;amp;&amp;amp; &amp;quot;abc&amp;quot;)&amp;lt;/script&amp;gt;"></div>

基本は同じですが "" の中に " がある場合外側の "" が '' に変わります
できるだけエスケープを減らしてくれてるようです
これのおかげで " のエスケープも <> 同様エスケープが解除されます

innerHTML は中身をテキスト的にどうこういじることはないのであまり困る機会はないと思います
それでも 微妙に違う独自の動きということを知っておいたほうがいいかもしれません

ところで " と ' の両方がある場合は " で囲んだ上でエスケープされたままになります
const text = "'" + '"'
document.body.innerHTML = '<div data-text="###"></div>'.replace(/###/, htmlEsc(text))
console.log(document.body.innerHTML)
<div data-text="'&quot;"></div>