const https = require("https");
const querystring = require("querystring");
const url = require("url");
const port = 10101;
// 1.创建代理服务https.createServer(onRequest).listen(port);
function onRequest(req, res) {
const originUrl = url.parse(req.url);
const qs = querystring.parse(originUrl.query);
const targetUrl = qs["target"];
const target = url.parse(targetUrl);
const options = {
hostname: target.hostname,
port: 80,
path: url.format(target),
method: "GET"
};
// 2.代发请求const proxy = https.request(options, _res => {
// 3.修改响应头const fieldsToRemove = ["x-frame-options", "content-security-policy"];
Object.keys(_res.headers).forEach(field => {
if (!fieldsToRemove.includes(field.toLocaleLowerCase())) {
res.setHeader(field, _res.headers[field]);
}
});
_res.pipe(res, {
end: true
});
});
req.pipe(proxy, {
end: true
});
}
事件委托是一种在父元素上设置事件监听器,以便在一个或更多的子元素上捕获事件的技术。当子元素上发生事件时,由于事件冒泡,这个事件会被推到父元素上,在父元素上的事件监听器就会被触发。在事件处理程序中,我们可以通过检查事件的目标元素来决定如何处理事件。
这是一个基本的JavaScript实现事件委托的例子:
javascriptdocument.querySelector('#parent').addEventListener('click', function(event) {
// 检查事件的目标元素是不是一个指定的子元素if (event.target.matches('.child')) {
console.log('子元素被点击');
}
});
在这个例子中,我们在ID为'parent'的元素上设置了一个click事件的监听器。当点击事件冒泡到'parent'元素时,这个监听器就会被触发。然后,我们通过event.target来检查被点击的元素是否是我们感兴趣的子元素(这里我们用.child来代表我们感兴趣的子元素)。如果是,那么我们就在控制台打印出消息。
在实际使用中,事件委托常用于处理大量子元素的情况。例如,在一个动态生成的列表中,我们可能不知道有多少个子元素,但我们可以在父元素上设置一个事件监听器,然后在事件处理程序中检查被点击的元素是否是我们感兴趣的子元素。这样,我们就不需要在每个子元素上都设置一个事件监听器,从而节省了资源。
JavaScript的垃圾收集(Garbage Collection,GC)机制是自动进行的,开发者不需要手动进行内存管理。这是JavaScript相比一些其他语言(如C++或Java)的一个主要优点。在JavaScript中,内存管理主要依赖于运行环境(或引擎),例如V8(Chrome和Node.js使用的引擎)或SpiderMonkey(Firefox使用的引擎)。
垃圾收集主要关注的是找出不再使用的变量,然后释放其内存。其核心思想是跟踪每个值的引用,然后找出不再被引用的值。
垃圾收集的过程通常包括以下步骤:
- 遍历所有的值,将它们标记为"可达"或"活跃"。
- 遍历所有的引用,找出那些指向可达值的引用。
- 对于每个引用的值,如果它以前没有被标记为可达,那么它现在被标记为可达。
- 对于每个被丢弃的值(即没有被标记为可达的值),垃圾收集器会释放其内存。
这个过程被称为"标记-清除"(mark-sweep)垃圾收集算法。还有一些其他的垃圾收集算法,例如"复制"(copy)和"标记-压缩"(mark-compact),但它们的基本思想是相同的。
垃圾收集对程序的性能和内存使用有显著的影响。首先,垃圾收集可能会暂停程序一段时间,这被称为"停顿"(jank)或"抖动"(churn)。如果垃圾收集过于频繁或持续时间过长,这可能会影响用户体验。因此,优化垃圾收集是许多JavaScript引擎的主要目标之一。例如,一些引擎使用内存分区(heap partitioning)来将内存分为许多小的区域,这样垃圾收集可以更快地完成,因为每个区域都小得多。
其次,垃圾收集对内存使用也有影响。如果垃圾收集过于频繁,或者没有有效地找出不再使用的内存,那么可能会浪费大量的内存。这可能会导致应用程序在运行时消耗更多的内存,从而降低性能。因此,优化垃圾收集算法和设置是非常重要的。
开发者还可以通过一些技术来改善垃圾收集的性能和内存使用,例如:避免创建大量的全局变量,使用对象属性替代变量,以及避免使用闭包等。
请注意,垃圾收集的性能和效果可能会因不同的JavaScript引擎而异,这是开发者需要考虑的一个重要因素。
高阶函数在JavaScript中指的是可以接受一个或多个函数作为参数,并/或返回一个函数的函数。这些高阶函数可以是任何类型的函数,包括匿名函数、箭头函数、或者任何你习惯使用的函数。
高阶函数在JavaScript中的应用非常广泛,它们可以用于封装代码、提高代码的可读性和可维护性、创建函数工厂和函数生成器等等。以下是一些具体的应用示例:
- 函数组合:你可以使用高阶函数将多个函数组合在一起。例如,你可以创建一个函数,这个函数接受几个函数作为参数,并返回一个新的函数,这个新的函数将这些输入函数组合在一起。
javascriptfunction combine(fn1, fn2, fn3) {
return function(arg1, arg2, arg3) {
return fn1(fn2(fn3(arg1), arg2), arg3);
}
}
- 函数映射和过滤:在处理数组时,高阶函数可以非常有用。例如,你可以使用高阶函数来实现数组的映射(map)、过滤(filter)和reduce等操作。
javascript// map
const numbers = [1, 2, 3, 4, 5];
const double = numbers.map(x => x * 2); // [2, 4, 6, 8, 10]
// filter
const even = numbers.filter(x => x % 2 === 0); // [2, 4]
- 函数柯里化:柯里化是一种处理高阶函数的编程技术,它允许你将一个多参数的函数转换成一系列单参数的函数。这在处理异步操作时特别有用。
javascriptfunction curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
以上只是高阶函数在JavaScript中的一些基本应用。实际上,高阶函数的使用范围远比这些广泛,它们是函数式编程的重要组成部分,也是JavaScript这个多范式语言的重要特性之一。
Promise和async/await在JavaScript中都是用来处理异步操作,但它们的使用方式和场景并不相同。
Promise是ES6中的一个内置对象,用于解决异步调用问题,可以解决多层回调嵌套的问题,提高代码可读性、更便于维护。
async/await是ES7新特性,是写异步代码的新方式,基于Promise实现,用于定义一个返回AsyncFunction对象的异步函数。它使得异步代码看起来像同步代码,这正是它的魔力所在。
Promise和async/await的区别:
- async/await只能用在异步函数中,而Promise可以用于异步和同步函数中。
- async/await是ES7新特性,基于Promise实现,非阻塞的,可以暂停、恢复、取消。而Promise是ES6中的对象,用于解决异步操作,不能用于普通的回调函数。
本文主要讨论前端开发中如何在JS文件中检测用户浏览器是否打开了调试面板。
debugger
一种常见的方法是使用 debugger,当打开开发者工具时会有 debugger 暂停,效果类似于程序中的断点,点继续调试的时候也是如此。
1 2 3 4 5 6 7 8 9 10 | <!DOCTYPE html> <body> <script> function check() { debugger; setTimeout(check, 1); } check(); </script> </body> |
破解方法
对于这种方法,我们只需要禁用 debugger 就行,最简单的方法就是点击开发中工具中的 deactivated breakpoints


禁用右键和 F12
禁用 F12 和右键,使得无法打开开发者工具。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!DOCTYPE html> <body> <script> // F12 window.onkeydown = window.onkeyup = window.onkeypress = function (event) { // 判断是否按下F12,F12键码为123 if (event.keyCode = 123) { event.preventDefault(); // 阻止默认事件行为 window.event.returnValue = false; } } // 右键 document.oncontextmenu = function () { event.returnValue = false; } </script> </body> |
破解方法
按下 ctrl+shift+I 或者点击 chrome 浏览器头像右侧的地方里面的更多工具->开发者工具
检测窗口大小变化
window.outerHeight 和 window.outerWidth 返回整个浏览器窗口的高度,宽度,包括侧边栏(如果存在)。window.innerHeight 和 window.innerWidth 返回浏览器视窗大小,如果有滚动条也包含滚动条。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | <!DOCTYPE html> <body> <script> const devtools = { isOpen: false, orientation: undefined, }; // inner和outer之间大小的阈值 const threshold = 170; // 定义监听开发者工具事件 const emitEvent = (isOpen, orientation) => { globalThis.dispatchEvent(new globalThis.CustomEvent('devtoolschange', { detail: { isOpen, orientation, }, })); }; const main = ({ emitEvents = true } = {}) => { const widthThreshold = globalThis.outerWidth - globalThis.innerWidth > threshold; const heightThreshold = globalThis.outerHeight - globalThis.innerHeight > threshold; const orientation = widthThreshold ? 'vertical' : 'horizontal'; if ( !(heightThreshold && widthThreshold) && ((globalThis.Firebug && globalThis.Firebug.chrome && globalThis.Firebug.chrome.isInitialized) || widthThreshold || heightThreshold) ) { // 有超过阈值 是打开的 if ((!devtools.isOpen || devtools.orientation !== orientation) && emitEvents) { emitEvent(true, orientation); } devtools.isOpen = true; devtools.orientation = orientation; } else { // 开发者工具未打开 if (devtools.isOpen && emitEvents) { emitEvent(false, undefined); } devtools.isOpen = false; devtools.orientation = undefined; } }; main({ emitEvents: false }); setInterval(main, 500); window.addEventListener('devtoolschange', event => { console.log(event.detail.isOpen) }); </script> </body> |
破解方法
破解方法就是将开发者工具设置为独立窗口,这样就无法检测到窗口变化。

重写 toString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //方法1 var x = document.createElement('div'); Object.defineProperty(x, 'id', { get:function(){ // 开发者工具被打开 } }); console.log(x); //方法2 var c = new RegExp("1"); c.toString = function(){ // 开发者工具被打开 } console.log(c); |
破解方法
对于一些使用 console 判断的可以把 console 的输出失效。此插件可以解决问题:https://github.com/546669204/fuck-debugger-extensions
可以在以下开源项目中了解更多相关解决方法。
https://github.com/AEPKILL/devtools-detector
JavaScript有许多库可以用来处理图像:
- Canvas API:这是HTML5标准的一部分,允许在网页上直接绘制图像。Canvas API提供了丰富的绘图功能,包括绘制形状、文本、图像和动画等。
- WebGL:这是一种在浏览器中渲染3D图形的方法。WebGL基于OpenGL ES 2.0,可以在浏览器中运行高性能的3D和2D图形应用程序。
- Three.js:这是一个JavaScript库,它使用WebGL实现3D图形渲染。Three.js提供了一个易于使用的API,使得创建复杂的3D场景变得容易。
- P5.js:这是一个用于创造性编程的JavaScript库。P5.js允许用户在网页上创建和发布互动艺术作品,包括动画、音乐和图形等。
- Fabric.js:这是一个强大的、灵活的JavaScript HTML5 Canvas库。Fabric.js提供了对象级别的操控和绘图,包括多种形状、文字、图片和效果等。
- Create.js:这是一个包含了EaselJS、TweenJS和SoundJS的JavaScript库。Create.js提供了一个简单易用的API,用于创建互动的HTML5内容。
- D3.js:这是一个用于创建数据驱动的文档的JavaScript库。D3.js允许用户使用HTML、SVG、CSS和其他Web标准来创建动态的、可交互的图表和图形。
v_jstools是一款浏览器插件,功能非常的nice。
工具地址:
https://github.com/cilame/v_jstools
一.下载文件
浏览器打开上面的网站后,点击code按钮,选择 Download ZIP 选项,将文件下载下来:
文件下载下来以后,复制到桌面,然后解压到当前文件夹,会看到一个 v_jstools-main 的文件夹。
二.插件安装
谷歌浏览器地址栏输入如下 url,打开扩展程序页面,并打开开发者模式:
chrome://extensions/
如果是其他浏览器,请使用菜单栏打开。
打开后,点击左上角的 加载已解压的扩展程序 按钮,然后选择 v_jstools-main 文件夹,如图:
在选择文件夹后,插件就已经加载进来了,如图:
三.插件功能
在扩展程序中,打开该插件,看看支持哪些功能:
第一步:
第二步:
打开配置页面后,来到这里:
可以看到,有三个功能。
第一个功能是 dom对象hook ,它内置了很多常见方法的hook,如eval,cookie,Function等,想hook哪个方法,直接打钩即可,非常的方便。有了这个,再也不用写油猴脚本啦。
第二个功能是 ast hook替换,顾名思义,就是拦截某个js代码,通过ast处理后,再返回ast处理后的代码,让混淆代码见鬼去吧。
它给的示例代码是这个:
function fetch_hook(code, url) {
var ast = parser.parse(code);
function removedebugger(path) {
path.replaceWith(t.identifier("/*debugger*/"))
}
traverse(ast, {
DebuggerStatement: removedebugger
});
var {code} = generator(ast, {
jsescOption: {
minimal: true,
}
});
return code
}
这里简单说一下, fetch_hook 函数 接受2个参数,可以不用理会,如果你想hook特定的url,可以在函数的首行加入这样一段代码:
if (url.indexOf("https://www.xxx.com.cn/xxx.js") == -1)
{
return code // 检测到不是这个 url 就直接返回原始代码
}
再就是编写ast插件了,按照这样下就好:
function removedebugger(path) {
path.replaceWith(t.identifier("/*debugger*/"))
}
traverse(ast, {
DebuggerStatement: removedebugger
});
先写方法,再遍历,也可以按照我星球里的方式进行编写,都是一样的。
不过在我使用了几次以后,还是觉得线下处理混淆代码香,当然要看你的使用场景了。
第三个功能是一些 AST的解密混淆,如图:
把混淆代码复制到上面的白色框中,点击对应的按钮即可解混淆,也是非常的方便。这里能解混淆的有 sojson,ob混淆,jsfuck以及压缩代码等,都是比较常用的功能。
试用了一番,可以还原最新版的ob混淆,效果还是很棒的。当然一些修改特征了的代码无法还原,这个要做到通杀确实有些难度。
示例代码:
javascriptfunction downloadImage(imageUrl, imageName) {
var a = document.createElement('a');
a.href = imageUrl;
a.download = imageName;
a.click();
}
// 使用
javascriptdownloadImage('http://example.com/example.jpg', 'example.jpg');
这种方法可能受到浏览器的安全限制,特别是在跨域请求的情况下。如果图片服务器不允许文件下载,那么这种方法可能会失败。
使用JavaScript的DOM操作。
javascript// 获取第一个图片元素
var img = document.getElementsByTagName('img')[0];
// 获取src属性
var src = img.src;
console.log(src);
在JavaScript中,可以使用HTML按钮元素的disabled属性来将其置灰。disabled属性是一个布尔属性,如果设置为true,则按钮将被禁用,看起来像是被置灰了。
示例代码:
html<button id="myButton">Click Me</button>
<script>
// 获取按钮元素
var button = document.getElementById("myButton");
// 将按钮置灰
button.disabled = true;
</script>
在JavaScript中,您可以使用window.location对象来重定向到另一个页面。例如,如果您想重定向到"https://zhe.ink",您可以使用以下代码:
javascriptwindow.location.href = "https://www.example.com";
当这行代码被执行时,用户的浏览器会被导向至"https://zhe.ink"。window.location.href会改变当前页面地址,但不会重新加载页面,这使得它可以用于实现页面的平滑跳转。
如果您想让页面完全刷新,您可以使用window.location.replace()方法,如下所示:
javascriptwindow.location.replace("https://www.example.com");
这种方法会导致浏览器的历史记录中保存新的页面地址,但是不会触发与页面刷新相关的浏览器事件。
要使用JavaScript的fetch API上传文件,您需要使用FormData对象和fetch函数。
演示如何使用fetch API上传文件:
html<form id="fileUploadForm">
<input type="file" id="fileInput">
<button type="submit">上传</button>
</form>
<script>
document.getElementById("fileUploadForm").addEventListener("submit", function(event) {
event.preventDefault(); // 阻止表单默认提交行为
var fileInput = document.getElementById("fileInput");
var file = fileInput.files[0];
var formData = new FormData();
formData.append("file", file);
fetch("/upload", { // 替换为您自己的上传接口地址
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
});
</script>
我们首先获取文件输入元素和表单元素,然后创建一个FormData对象并将文件添加到其中。
接下来,我们使用fetch函数将表单数据发送到服务器。请注意,我们使用了POST方法并将表单数据作为请求体发送。
最后,我们使用then函数处理服务器响应并将结果输出到控制台。
要阻止表单使用页面刷新方式重复提交,您可以使用JavaScript中的一些技术来禁用表单的提交按钮,直到表单提交完成后再重新启用。
示例代码:
html<form id="myForm">
<!-- 表单内容 -->
<input type="submit" value="提交" id="submitButton">
</form>
<script>
document.getElementById("myForm").addEventListener("submit", function(event) {
// 禁用提交按钮
document.getElementById("submitButton").disabled = true;
});
</script>
我们为表单添加了一个事件监听器,当表单提交时,禁用提交按钮。这样,即使用户刷新页面或点击提交按钮多次,表单也不会重复提交。
在JavaScript中,可以使用window对象的onbeforeunload事件来检测页面刷新事件。
以下是一个简单的示例代码:
javascriptwindow.onbeforeunload = function(event) {
event.preventDefault();
event.returnValue = "确定要离开页面吗?";
};
在这个例子中,当用户尝试刷新或关闭页面时,onbeforeunload事件会被触发,然后调用一个函数。在这个函数中,我们阻止了默认行为(即刷新或关闭页面),并设置了event.returnValue属性,以在浏览器中显示一个警告消息。
请注意,这个事件只在用户尝试刷新或关闭页面时才会触发。如果用户点击了一个链接或执行了其他导航操作,这个事件不会触发。
在JavaScript中,你可以使用navigator.clipboard.writeText API来将文本复制到剪贴板。这个API返回一个promise,如果复制操作成功,该promise将解析为undefined。
以下是如何使用此API的一个简单示例:
javascriptasync function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('复制成功!');
} catch (err) {
console.error('复制失败:', err);
}
}
copyToClipboard('这是我要复制的文本');
请注意,navigator.clipboard.writeText 只在浏览器允许访问剪贴板时才可用。在某些情况下,例如在某些浏览器或安全设置较高的环境下,这可能不可用。
此外,为了使用这个API,你的网站需要获取用户的明确许可才能访问剪贴板。这是由浏览器的剪贴板访问权限策略决定的。
为了进行SEO优化,为每个页面编写具有针对性且准确的内容描述和关键词是重要的。这样做可以帮助搜索引擎更好地理解页面的主题和内容,提高页面的可见性和排名。
然而,仅仅为了SEO而简单地将关键词填充到描述中并不一定是最好的做法。重要的是要确保描述和关键词与页面内容相关且自然地融入文本中,而不是过度堆砌关键词。这样做可能会让搜索引擎认为页面是过度优化或堆砌关键词,从而导致惩罚或忽略该页面。
此外,要注意的是,搜索引擎已经足够智能,能够通过分析页面内容和链接等多种因素来确定页面的主题和关键词。因此,尽管为每个页面编写描述和关键词是有帮助的,但更重要的是确保整个网站的内容和结构都是一致的,并且能够清晰地传达给搜索引擎和用户。
- fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; #脚本名称。
- fastcgi_param PATH_TRANSLATED document_rootfastcgi_script_name; #请求的路径。
要获取农历信息,你可以使用JavaScript中的日期对象。然而,需要注意的是,JavaScript中的日期对象仅提供公历(格里高利历)信息,不包含农历信息。要获取农历信息,你可能需要使用一些第三方库,如Lunr.js或ChineseDateFormat。
这是一个使用Lunr.js来获取农历信息的简单例子:
首先,你需要在你的项目中安装Lunr.js。如果你使用npm,你可以这样做:
bashnpm install lunr
然后,你可以这样使用Lunr.js来获取农历信息:
javascriptimport Lunr from 'lunr';
// 创建lunr索引
var index = Lunr(function () {
this.ref('id');
this.field('title');
this.field('content');
// 你的数据
var data = [
{id: '1', title: '公历1990年1月1日 对应的农历为何年何月何日', content: '公历1990年1月1日对应的农历是1989年十一月二十九'},
{id: '2', title: '公历1991年1月1日 对应的农历为何年何月何日', content: '公历1991年1月1日对应的农历是1990年十一月十八'},
{id: '3', title: '公历1992年1月1日 对应的农历为何年何月何日', content: '公历1992年1月1日对应的农历是1991年十一月廿六'},
// ... 更多的数据
];
// 将数据添加到lunr索引中
for (let i = 0; i < data.length; i++) {
this.add(data[i]);
};
});
// 查找结果
console.log(index.search('公历2023年1月1日 对应的农历为何年何月何日')[0].ref); // 输出: "3"
以上代码会返回你在数据中设置的的所有包含"公历2023年1月1日 对应的农历为何年何月何日"的索引项的id。
请注意,以上示例需要你自己提供所有日期对应的农历信息,这在某些情况下可能会非常冗长。另外,请确保你的日期格式正确,并且所有可能的日期都包含在你的数据中。如果需要搜索的日期不在你的数据中,将无法返回任何结果。
myDiv.addEventListener('touchstart', function (e) {
console.log('开始touch');
e.preventDefault()
startX = e.touches[0].screenX
startY = e.touches[0].screenY
}, false);
myDiv.addEventListener('touchend', function (e) {
e.preventDefault()
endX = e.changedTouches[0].screenX
endY = e.changedTouches[0].screenY
console.log(startY);
console.log(endY);
let moveX = endX - startX;
let moveY = endY - startY;
if ((Math.abs(moveX) > 40 || Math.abs(moveY) > 40)) {
// 判断X轴移动的距离是否大于Y轴移动的距离
if (Math.abs(moveX) > Math.abs(moveY)) {
// 左右
console.log(moveX > 0 ? '左' : '右')
} else {
// 上下
console.log(moveY > 0 ? '上一个' : '下一个')
}
}
}, false);
myDiv.addEventListener('mousedown', function (e) {
console.log('开始touch');
startX = e.clientX
startY = e.clientY
});
myDiv.addEventListener('mouseup', function (e) {
endX = e.clientX
endY = e.clientY
console.log(startY);
console.log(endY);
let moveX = endX - startX;
let moveY = endY - startY;
if ((Math.abs(moveX) > 40 || Math.abs(moveY) > 40)) {
// 判断X轴移动的距离是否大于Y轴移动的距离
if (Math.abs(moveX) > Math.abs(moveY)) {
// 左右
console.log(moveX > 0 ? '左' : '右')
} else {
// 上下
console.log(moveY > 0 ? '上一个' : '下一个')
}
}
});