最近,解决了一个网络视频学习的两个问题:过10分钟就暂停并显示提示窗口、不能自动跳到下个章节,思路是:在控制台上运行一段编写的脚本代码,重定义该网页上一个函数(解决人工确认问题);加挂链接click事件获取打开网页的window对象,然后定时播放未完成的章节(解决连续播放问题)。在此同时,发现该网站前端网页的数据与函数(脚本代码)等缺乏基本保护措施,能够被轻松地在浏览器控制台上重定义或修改。(呵呵,自己做的若干网站系统同样存在这类问题。)
网页数据和函数总是某个对象的属性或方法(const声明的全局变量也是某个全局对象的属性,只是不能访问该全局对象),ECMA-262给出的Object.defineProperty()函数可以定义数据或函数可配置、枚举和修改的一组属性,从而有效解决浏览器控制台修改数据和重定义函数两个问题。
一、使用const申明函数
ES6新增的块级申明const可以有效解决全局(指<script></script>)或某个对象中的函数成员被重定义的问题,代码举例如:const show = function(){...}。如果在控制台重定义全局或某个对象的show函数将报错误。
该方法存在一个较大问题是,全局申明的函数不能做到跨框架(frames)调用,因为它不是window的函数,而是某个未公开的全局对象的函数。当然,可以将该方法赋值给window,如window.show = show,然后再调用。但此时的window.show还是面临控制台重定义的问题。
二、设定数据或函数的configurable、enumerable和writable属性
window或某个对象的数据或函数,可以使用Object.defineProperty()函数设定其内置boolean属性configurable、enumerable和writable为true/false(其中,enumerable为true,可枚举到当前属性),从而有效保护数据或函数被修改或重定义。代码举例如下:
var show = function(){console.log('show.')};
show(); // 显示 show.
Object.defineProperty(window, 'show', {configurable: false, enumerable: true, writable: false});
show = function(){console.log('show...');};
show(); // 仍然显示 show.上述重定义无效,虽然未报异常。
全局数据、某个对象的数据或函数均可类似处理。
关于Object.defineProperty()及相关函数可以看参看红宝书《Javascript高级程序设计》(第4版),下面给出一段直接定义的、不可重定义的函数代码:
Object.defineProperty(window, 'show', {enumerable: true, configurable: false, writable: false, value: function(){console.log('show.');
}});show(); // 显示 show.
show = function(){console.log('show...');};
show(); // 仍然显示 show.,重定义函数无效。Object.defineProperty(window, 'show', {writable: true}); // 再次修改为可改写的将报异常,因为configurable为false。
上述代码中,value可以是一个数据值,也可以是function。 注意,configurable要设置成false,此时不能再次修改configurable等属性值,而writable如果初次设置成true,则还可以修改为false,再改将报异常。
三、保护网页元素<input>的value值
网页元素<input>常用于保存一些脚本代码可以读写的值,如:<input id="hNo" type="hidden", value="1" />。显然,该值可以在浏览器控制台上编程修改,保护方法为:读取值到window中,然后remove该元素(非必须)。代码举例如:
var v = document.getElementById('hNo');
var hNo = v.value;
Object.defineProperty(window, 'hNo', {configurable: false, enumerable: true, writable: false}); // 设置值修改无效
v.remove(); // 在DOM中删除该元素
结语:前端网页需要保护的,除脚本函数和数据之外还有很多,比如BOM和DOM模型中对象的属性和方法(常见如XMLHttpRequest的属性和方法),笔者将在后续文章中继续探讨该类问题。
后记:Object.freeze()方法可以冻结一个类型或对象中的全部属性和方法,使得该类型的属性和方法不可再修改或删除等,效果类似本文前述的操作。如果需要保护整个类型或对象的数据和方法,使用freeze()即可。