Предположим, я пишу сценарий букмарклета для изменения веб-страницы. Большинство веб-сайтов (использующих веб-пакет и т. д.) имеют примерно такую структуру:
<html>
<script type = "text/javascript">
const buttonClicked = (() => {
let internal_variable = 0;
const _internal_buttonClicked = () => {
internal_variable++;
document.getElementById("myButton").innerText = `Clicked ${internal_variable} times!`;
};
return _internal_buttonClicked.bind(this);
})();
</script>
<body>
<button id = "myButton" onclick = "buttonClicked()">Clicked 0 times!</button>
</body>
</html>
Я хотел бы получить доступ к internal_variable и установить для него значение -100. С отладчиком это просто; Я делаю точку останова внутри _internal_buttonClicked(), а затем меняю internal_variable напрямую.
Как я могу сделать это без
Я знаю, что простой ответ — «вы не можете намеренно», но я готов принять любые обходные решения, решения, включающие расширения браузера/привилегированное выполнение, решения, специфичные для require и т. д.
Связанный:
Я поощряю это, хотя знаю, что хорошего решения может и не быть. Я чувствую, что есть много качественных ответов о том, как отлаживать, исправлять или изменять двоичный файл, и не так уж много информации о том, как сделать то же самое с JS, и я надеюсь узнать, как подойти к этому чрезвычайно -Общая ситуация немного лучше. Если решения действительно нет, то документирование причин (я полагаю, есть проблемы с XSS?) будет следующим лучшим решением.
Если все ссылки на эту функцию находятся в атрибутах HTML onclick, просто определите новую функцию и вместо этого сделайте ссылку на нее в этих атрибутах. Будет ли этого достаточно?
Ну, «решения, включающие расширения браузера/привилегированное выполнение» — это совершенно другой пример, чем «букмарклет». Посмотрите chrome.debugger/протокол удаленной отладки.
Этот вопрос касается именно встроенных скриптов?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Невозможно присвоить новое значение internal_variable, поскольку оно не входит в область действия вашего кода. Вы также не можете назначить новую функцию buttonClicked, поскольку она является константой.
Но в зависимости от реальной ситуации вы можете использовать другие подходы. Вот некоторые из них, которые подойдут для вашего примера:
innerTextПопросите его изменить число, отображаемое на кнопке, всегда вычитая 100 из значения, которое должно отображаться.
Ваш код будет иметь это:
Object.defineProperty(document.getElementById("myButton"), "innerText", {
set(text) {
// Subtract 100 from the number in the text "Clicked <number> times!"
text = text.replace(/(Clicked )(\d+)( times!)/, (_, a, b, c) => a + (b - 100) + c);
// Call the prototype setter for innerText
Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerText").set.call(this, text);
}
});
Сведено в букмарклет:
javascript: {Object.defineProperty(document.getElementById("myButton"),"innerText",{set(text) {text=text.replace(/(Clicked )(\d+)( times!)/,(_,a,b,c)=>a+(b-100)+c);Object.getOwnPropertyDescriptor(HTMLElement.prototype,"innerText").set.call(this, text);}});}
Это похоже на предыдущее решение, но изменение текста производится после того, как он уже записан на кнопку innerText. Добавьте обработчик кликов (который будет выполняться после уже подключенного), который выполняет это исправление:
document.getElementById("myButton").addEventListener("click", function () {
this.innerText = this.innerText.replace(/(Clicked )(\d+)( times!)/, (_, a, b, c) => a + (b - 100) + c);
});
Сведено в букмарклет:
javascript: document.getElementById("myButton").addEventListener("click",function(){this.innerText=this.innerText.replace(/(Clicked )(\d+)( times!)/,(_,a,b,c)=>a+(b-100)+c);})
buttonClicked2Там должен быть код с модификацией счетчика. Затем измените каждый вызов с buttonClicked на buttenClicked2. Здесь я предполагаю, что это похоже на пример, где такая ссылка присутствует только в атрибуте onclick кнопки. Чем больше мест выполняется этот вызов, тем больше функций вам может потребоваться скопировать как новые функции, перемонтировав также ссылки на эту функцию и т. д. Но здесь затрагивается только атрибут onclick:
var buttonClicked2 = (() => {
let internal_variable = -100; // Our update
const _internal_buttonClicked = () => {
internal_variable++;
document.getElementById("myButton").innerText = `Clicked ${internal_variable} times!`;
};
return _internal_buttonClicked.bind(this);
})();
// Redirect the click
document.getElementById("myButton").setAttribute("onclick", "buttonClicked2()");
Сведено в букмарклет:
javascript: {var buttonClicked2=(()=>{let internal_variable=-100;const _internal_buttonClicked=()=>{internal_variable++;document.getElementById("myButton").innerText=`Clicked ${internal_variable} times!`;};return _internal_buttonClicked.bind(this);})();document.getElementById("myButton").setAttribute("onclick", "buttonClicked2()");}
<iframe>В него <iframe> поместите текущий HTML, но с модификацией кода, который вы хотите иметь. Итак страница перезагрузится, но с вашей модификацией и в <iframe>.
if (window === top) {
// Patch the HTML
let html = document.documentElement.innerHTML
.replace("internal_variable = 0;", "internal_variable = -100;");
let iframe = `<body style = "margin:0"><iframe style = "display:block;border:0;height:100%;width:100%"></iframe></body>`;
// Replace the current document with just an <iframe>
document.write(iframe);
// Write the patched HTML to the frame document
document.querySelector("iframe").srcdoc = html;
}
Сведено в букмарклет:
javascript: if (window===top){let html = document.documentElement.innerHTML.replace("internal_variable = 0;", "internal_variable = -100;");let iframe = `<body style = "margin:0"><iframe style = "display:block;border:0;height:100%;width:100%"></iframe></body>`;document.write(iframe);document.querySelector("iframe").srcdoc=html;}
Каждое из вышеперечисленных решений имеет свои ограничения. Подходит ли один из них в реальных случаях, во многом будет зависеть от функциональности страницы.
Огромное спасибо за указание награды. Я очень надеюсь, что некоторые части этого ответа были для тебя полезны, @Kaia.
Это невозможно.