Skip to content

Latest commit

 

History

History
72 lines (48 loc) · 2.68 KB

2019-03-06.md

File metadata and controls

72 lines (48 loc) · 2.68 KB

怎样找回原生方法

今天一个后端同事找我帮忙看一个后台的前端问题

问题其实很简单,就是下面这行代码并没有正常执行(就是网页没有出一个确认弹窗请求用户确认)

if (confirm('你确定要取消订单吗?') { // 取消订单 }

通过断点发现 confirm 方法被重写(Override)了

因为后台是用一个很老的 UI 框架搭建的,平时都是后端人员在维护,大部分脚本都是在 script 标签中直接写的,变量、函数全是暴露在全局作用域,加上每个后端对 JS 的了解参差不齐,所以会写出一个和全局方法同名的函数也不足为奇吧

最后当然是重命名之前定义的 confirm 函数来解决这个问题

事后,我在想,还有其他方法解决这个问题吗?

比如这么一个场景:我提供一个插件脚本给别的开发者异步载入并调用里面的方法,而这个方法刚好就要用到浏览器原生的方法

Plugins.prototype.alert = function (message) {
	window.alert(message);
}

显然,如果别的开发者在我的插件载入之前就重写了 alert 事件,那这个方法就实现不了「我预期的效果」了

这时候,除了在文档中提醒调用者不用重写哪些原生方法外,还有别的方法可以「找回」原生的方法吗?

确实还有一个并不完美的方法

原理是通过插入一个 iframe 来获得一个像「新的一样」的浏览器上下文

const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);

然后修改一下插件的 alert 方法

Plugins.prototype.alert = function (message) {
	const iframeAlert = iframe.contentWindow.alert.bind(window);
	const alert = window.alert;
	const nativeAlert = alert
							? alert.toString() === 'function alert() { [native code] }' 
								? alert 
								: iframeAlert 
							: iframeAlert;

	nativeAlert(message);
}

注意要将 iframe 的方法拿过来的时候要绑定当前页面的上下文,比如 querySelector 方法就需要

querySelector = iframe.contentDocument.querySelector.bind(document);

否则,选择的是 iframe 文档的元素

如果 document.createElement 也被重写了呢?

还可以用 HTMLDocument.prototype.createElement 凑合

但是如果也这个也被重写了的话,那就没办法了,这就是为什么这个方法并不完美

参考链接