推荐文章
热门文章
公告展示
最新发布
-
免费的轻量级代码编辑器 HBuilderX HBuilderX 是由DCloud开发的一款面向Web前端开发者的集成开发环境(IDE),它是HBuilder的下一代产品,继承了HBuilder的诸多优点,并在此基础上进行了全面的升级。HBuilderX旨在提供更加强大、高效的开发体验,支持多种前端技术栈,包括但不限于HTML5、CSS3、JavaScript、Vue.js、React、Angular等。 功能特点 快速启动:启动速度快,能够在几秒内启动,这对于频繁切换任务的开发者来说是非常有用的特性。 智能提示:提供了丰富的代码补全和提示功能,能够提高编写代码的速度和准确性。 实时预览:支持实时预览功能,开发者可以直接在编辑器中看到代码修改的效果,无需频繁切换浏览器。 多页面编辑:支持多页面同时编辑,可以在同一个窗口中打开多个文件,方便进行对比和同步修改。 多光标操作:支持多光标编辑,允许用户一次编辑多处相同代码,提高编码效率。 HTML5/CSS3支持:内置对HTML5和CSS3的支持,包括新特性的语法高亮和代码提示。 Git集成:内置Git支持,方便开发者进行版本控制和团队协作。 代码美化:提供代码格式化功能,帮助开发者保持代码整洁。 文件压缩打包:支持JavaScript、CSS文件的压缩合并,减少HTTP请求次数,提高网页加载速度。 调试工具:拥有内置的调试工具,便于开发者进行前端代码的调试。 扩展插件:支持插件扩展,用户可以根据需要安装额外的插件来增强IDE的功能。 模板支持:内置了多种模板,包括常用的框架模板,方便快速搭建项目。 下载及安装 HBuilderX官网下载地址:https://www.dcloud.io/hbuilderx.html,可以在下载页面选择Windows或MacOS平台,Windows平台压缩包下载后解压即可运行。 HBuilderX首次运行时会出现主题和快捷键选择窗口,可以选择喜欢的主题和习惯的操作方式。点击【开始体验】按钮后,可以在HBuilderX自述文件中查看HBuilderX的特性。 图片 基本操作 语法提示:工具提供多种框架语法提示库,可以在窗口右下角进行选择。 图片 代码助手:工具提供智能的代码提示助手,可按【ctrl+I】打开代码助手,代码提示后可以按【alt+数字】选择直接选择某个项目。 图片 语法帮助:在编辑区域鼠标悬停会自动弹出语法或说明提示,按下【F1】键可以跳转到官方手册(2.5.7以上版本会在外部浏览器打开说明文档)。 图片 多光标操作:在编辑区域按【ctrl+鼠标左键】可增加一个光标,【ctrl+鼠标右键】可取消一个光标或选区。 图片 相同词操作:在编辑区域按【ctrl+shift+L】可以选择和光标位置相同的所有词同时进行编辑。 列选择:在编辑区域使用【alt+鼠标拖选】进入列选择编辑方式。 图片 Emmet支持:Emmet语法可以用缩写来动态生成所需要的结构及样式,从而极大改善HTML和CSS的编写流程。 图片 智能双击: 双击引号/括号内侧,选中引号/括号内的内容 双击逗号两侧,选择逗号前一段或后一段 双击行尾,选中该行(不含回车符) 双击连词符(-_)选中整个词 双击折叠行首内容开头,选择折叠段落 双击行首缩进,选择相同缩进的段落 双击列表符号,选择列表段落 双击Tag开头或结尾,选择整段Tag 双击属性赋值等号=,选择Html属性 双击if、function等关键字,选择整段包围区域 双击分号,选择js等语言的;分号前段落 双击css类名左侧,选择Css类 双击注释符选择注释区域 双击#选择markdown标题段落 双击语法定义符开头选择markdown图片、超链接、加粗、倾斜、代码等语法区 还有更多的操作和编辑技巧可以查看官方文档:https://hx.dcloud.net.cn 另外,软件对vue开发做了大量优化,新版本仅支持uni-app、uni-app cli、vue2-cli、vue3-cli项目中可以切换vue语法提示库;对Markdown文件编辑提供了强大支持,支持Emmet、快捷键编辑、智能粘贴、路径提示、Mermaid流程图等;对Json编辑进行了优化;支持开发小程序;可以创建uni-app项目;支持java插件、nodejs插件,并兼容了很多vscode的插件及代码块。 总之,HBuilderX非常适用于Web前端开发人员,特别是在进行HTML5相关的项目开发时非常高效。此外,由于其对多种语言的支持,也可以作为通用的轻量级开发工具使用。
-
你不知道的 7 项 CSS 新功能 CSS 一直在进步,不断推出新的功能,让开发更加强大、简单、有趣。以下是一些最新的 CSS 更新,它们不仅能优化你的工作流程,还会改变你对 CSS 的使用方式。我们也会通过一些示例帮助你理解这些功能的应用场景。 1. 无需 Flexbox 或 Grid 的快速居中对齐 还记得以前用 CSS 居中元素的“痛苦”吗? 现在,有了 align-content 属性,居中对齐变得轻松无比,无需额外的容器。 .my-element { display: block; align-content: center; }再也不用纠结是选 flexbox 还是 grid 了。 2. 更智能的 CSS 变量:@property CSS 变量非常灵活,但也存在缺陷,比如缺乏类型约束和默认值支持。 现在,@property 规则让你可以定义具有特定类型、继承规则和初始值的自定义属性。 传统做法: :root { --rotation: 45deg; } div { transform: rotate(var(--rotation)); }如果意外赋值 --rotation: blue;,代码可能会报错或行为异常。 新方法: @property --rotation { syntax: '<angle>'; inherits: false; initial-value: 0deg; } div { transform: rotate(var(--rotation)); } syntax: 限定变量值类型,比如 <angle> 表示角度。 inherits: 决定变量是否继承父级值。 initial-value: 定义默认值(这里是 0deg)。 这不仅提升了代码的健壮性,也减少了意外错误。 3. @starting-style:解决页面加载时样式闪烁问题 开发者经常遇到页面加载时内容闪烁的问题(FOUC)。 @starting-style 提供了解决方案,可以为元素设置初始样式,避免布局错乱。 @starting-style { .modal { opacity: 0; visibility: hidden; } } .modal { opacity: 1; visibility: visible; transition: opacity 0.3s ease, visibility 0.3s ease; }这样,即使在页面加载时,也能保证模态框的显示效果流畅自然,无需繁琐的内联样式。 4. 数学功能升级:更多强大的计算方法 以前,calc() 是 CSS 唯一的数学函数,但功能有限。 现在,新增了 round()、mod() 和 rem() 等强大函数,大幅扩展了计算能力。 传统方式: .element { width: calc(100% - 50px); }新功能: .box { /* 四舍五入为 3px */ margin: round(2.5px); } .stripe:nth-child(odd) { left: calc(var(--index) * 50px mod 200px); } .circle { /* 输出 1px */ width: rem(10px, 3px); }这些新增函数让复杂的布局计算变得更加灵活且直观。 5. 光明与黑暗的完美切换:light-dark() 定义明暗主题的样式曾经需要依赖媒体查询,容易导致代码重复。 现在,light-dark() 函数简化了这一过程。 传统方法: body { background-color: white; color: black; } @media (prefers-color-scheme: dark) { body { background-color: black; color: white; } }新方法: body { background-color: light-dark(white, black); color: light-dark(black, white); } 第一个值是浅色模式的样式。 第二个值是深色模式的样式。 再也不用重复代码,主题切换更加高效。 6. 表单验证新伪类::user-valid 和 :user-invalid 以前,:valid 和 :invalid 的表单验证伪类会在页面加载时触发,导致样式误判。 新伪类 :user-valid 和 :user-invalid 专注于用户交互后的状态,大幅提升体验。 新方法: input:user-valid { border-color: green; } input:user-invalid { border-color: red; }这避免了未交互前就出现错误提示的问题,让表单验证更贴合实际需求。 7. interpolate-size:更顺滑的尺寸动画 CSS 一直以来对高度、宽度等尺寸的动画支持不够友好。 现在,有了 interpolate-size 属性,尺寸动画变得更加平滑自然。 传统方法: .collapsible { overflow: hidden; max-height: 0; transition: max-height 0.3s ease-out; } .collapsible.open { max-height: 500px; }新方法: .panel { interpolate-size: height 0.5s ease; } .panel.open { height: auto; }浏览器会动态计算起始和结束尺寸,无论内容多少,动画都能流畅衔接。 总结 这些 CSS 新功能都得到了主流浏览器的支持,无论是 Chrome、Firefox 还是 Safari,都可以立即开始使用。掌握这些特性不仅能提升你的工作效率,还能让代码更加简洁优雅。
-
npm最新国内镜像源设置 国内源 淘宝:https://registry.npmmirror.com/ 腾讯云:https://mirrors.cloud.tencent.com/npm/ CNPM:https://r.cnpmjs.org/ 设置 #查询当前使用的镜像源 npm get registry #设置为淘宝镜像源 npm config set registry https://registry.npmmirror.com/ #验证设置 npm get registry #还原为官方源 npm config set registry https://registry.npmjs.org/
-
PHP 实现 Wget 在线仿站工具 引言 项目的核心是使用PHP处理用户请求,通过SSH连接服务器执行爬取命令,并将结果发送到用户邮箱。 功能概述 该工具具备以下功能: 输入有效的URL和邮箱。 验证URL格式。 通过SSH连接执行Wget命令抓取网页。 生成ZIP文件并通过邮箱通知用户。 页面结构 页面使用Bootstrap框架实现响应式设计。以下是页面的基本HTML结构示例: <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>在线仿站工具</title> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css"> </head> <body> <nav class="navbar sticky-top navbar-expand-lg navbar-light bg-white border-bottom"> <div class="container"> <a class="navbar-brand" href="./">在线仿站工具</a> </div> </nav> <div class="container mt-5"> <div class="row"> <div class="col-md-8 offset-md-2"> <input type="text" id="url" class="form-control" placeholder="请输入有效的网址" required /> <input type="text" id="email" class="form-control mt-2" placeholder="请输入有效的邮箱" required /> <input id="submit" type="button" value="提交任务" class="btn btn-primary btn-block mt-3" /> </div> </div> </div> <script src="./assets/js/common.js"></script> </body> </html>图片 后端逻辑 后端使用PHP实现,主要功能集中在api.php文件中。以下是该文件的核心代码示例: 1. 请求处理 首先,我们检查请求方法是否为POST,并获取URL和邮箱: <?php if ($_SERVER['REQUEST_METHOD'] !== 'POST') { exit(json_encode(array('code' => '-1', 'msg' => '仅支持POST请求'), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); } $url = $_POST['url']; $email = $_POST['email'];2. URL验证 使用正则表达式验证输入的URL格式,确保用户输入的是有效的URL: $preg = "/^http(s)?:\\/\\/.+/"; if (!preg_match($preg, $url)) { log_error("Invalid URL format: $url"); exit(json_encode(array('code' => '-1', 'msg' => '域名请带上协议头!如(http:// 或 https://)'), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); }3. 生成文件名 生成唯一的文件名以便于后续的下载: $timestamp = time(); $file = "website_$timestamp.zip";4. 执行SSH命令 通过自定义的SSH类连接到服务器并执行Wget命令: $ssh = new Components_Ssh($host, $user, $pass, $port, $log); $command = escapeshellcmd("bash /www/wwwroot/{$site_url}/wget_site.sh {$url} {$file} >/dev/null && echo \"success\""); $result = $ssh->cmd($command);5. 检查文件生成状态 检查文件是否成功生成,若未生成,则记录错误信息: if (!file_exists('./down/' . $file)) { log_error("File generation failed for: $url"); exit(json_encode(array('code' => '-1', 'msg' => '爬取失败,请稍后再试。'), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); }6. 发送邮件通知 发送爬取结果到用户邮箱的代码如下: $content = '爬取成功,下载链接:' . $site_url . '/down/' . $file; $wz = $smtpapi . "?adress=" . urlencode($email) . "&isHTML=false&title={$site_url}爬取成功&content=" . urlencode($content); $response = @file_get_contents($wz);7. 错误日志记录 使用自定义的log_error函数记录所有错误信息,确保系统的可维护性: function log_error($message) { error_log(date('Y-m-d H:i:s') . " - " . $message . "\n", 3, '/path/to/error.log'); }8. SSH连接类 以下是一个简单的SSH连接类示例,供后续使用: class Components_Ssh { private $connection; public function __construct($host, $user, $pass, $port = 22) { $this->connection = ssh2_connect($host, $port); ssh2_auth_password($this->connection, $user, $pass); } public function cmd($command) { $stream = ssh2_exec($this->connection, $command); stream_set_blocking($stream, true); return stream_get_contents($stream); } }9. Wget脚本示例 wget_site.sh脚本负责执行实际的爬取操作,代码示例如下: #!/bin/bash url=$1 file=$2 wget --mirror --convert-links --adjust-extension --page-requisites --no-parent $url -P /www/wwwroot/your_site/down/ zip -r /www/wwwroot/your_site/down/$file /www/wwwroot/your_site/down/*10. 处理返回结果 处理执行命令后的返回结果以便后续使用: if ($result === 'success') { exit(json_encode(array('code' => '1', 'msg' => '爬取任务已提交,请查看邮箱。'), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); } else { log_error("Command execution failed: $command"); exit(json_encode(array('code' => '-1', 'msg' => '命令执行失败,请重试。'), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); }11. 提交任务前的提示 在前端添加任务提交前的提示,增强用户体验: document.getElementById('submit').onclick = function() { const url = document.getElementById('url').value; const email = document.getElementById('email').value; if (url === '' || email === '') { alert('请填写所有字段!'); return; } fetch('api.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `url=${encodeURIComponent(url)}&email=${encodeURIComponent(email)}` }) .then(response => response.json()) .then(data => { if (data.code === '-1') { alert(data.msg); } else { alert('任务已提交,请查看您的邮箱!'); } }); };12. 邮件发送状态检查 检查邮件发送状态以确保用户能够及时收到通知: if ($response === false) { log_error("Email failed to send to: $email"); exit(json_encode(array('code' => '-1', 'msg' => '邮件发送失败,请检查邮箱地址。'), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); }核心技术 PHP:后端逻辑处理,包括请求处理和SSH命令执行。 SSH:使用SSH连接到服务器进行远程操作。 Wget:爬取网页及其资源的工具。 Bootstrap:用于简化前端布局和样式。 总结 这个在线仿站工具允许用户快速爬取并下载网站资源。关键在于安全地处理用户输入、稳定地执行后端爬取操作,并确保系统的可维护性。
-
CSS奇思妙想:动态点亮文本下划线 一、简介 在现代网页中,为用户提供流畅且具有吸引力的交互体验至关重要。本文分享了一种纯CSS实现的动态文本下划线效果。当鼠标悬停在文本上时,无论文本的宽度如何、有多少行、行中有多少字,下划线都会以从左到右、从上到下的顺序,自适应的在文本底部动态、平滑地显示出来。而当鼠标移出时,下划线则按照相反的方向,逐行动态、平滑地隐藏,直到恢复至最初状态。具体效果如下: 案例效果 图片 该动画效果看起来很简单,具体实现也确实不难,难的是实现思路,需要扎实的前端CSS基础和很大的脑洞。该动画效果中利用到了:行内元素的基础特性、元素背景特性、trasition过渡等CSS的基础知识。 该动画可用于资料列表、新闻列表等页面场景中,可以突出显示鼠标所聚焦的文本内容,提升用户的视觉交互体验。 二、具体实现 1、实现思路 ① 看到文本下划线,我们首先会考虑通过text-decoration属性来实现,将文本的该属性默认状态设置为none,然后在hover的时候将其设置为underline,使文本出现下划线。但仔细想想,会发现这个方案并不可取,因为text-decoration是一个离散属性,只能在不同属性值状态之间切换,无法通过trasition过渡实现平滑动画。 ② 排除掉text-decoration属性之后,我们继续思考还有什么样式属性可以实现类似下划线的效果。此时应该想到两个属性:border和background。然后进一步思考,发现border只能控制宽度或者颜色,而且每一行的border只能同时显示,或者同时隐藏,无法实现逐行按序显示/隐藏下划线的效果。排除掉border后,最终确定需要依赖background属性来实现目标效果。 ③ 确定属性之后,首先我们要使用行内元素(或设置display: inline;的其他元素)来承载文本内容。因为行内元素具有一个重要的特性:元素的宽度和高度由其内容决定,当文本内容换行时,从布局角度看,每个换行后的文本片段会被视为单独的内联内容部分,而不是一个整体的块级元素那样有明确的整体宽度和高度概念。这一点特性,在后面通过背景实现下划线效果时将会起到关键作用。 ④ 接下来思考应该如何设置背景属性。目标效果中下划线是纯色的,首先想到的应该是设置背景颜色,但这种想法是错误的。因为设置背景颜色会直接应用到整个行内元素内容区域,而不是对每行的内联文本片段进行单独设置,且无法控制尺寸和位置。但如果给行内元素设置的背景图片,则会基于每行的内联文本片段来进行处理,而非整体内容区域,并且可以通过 background-size 和 background-position 属性进行精确控制。简单来说:我们给换行的行内元素设置背景图片时,其实际效果并非给整个内容块设置背景,而是给每行的内联文本片段设置背景。 ⑤ 经过前面四步的思考,我们已经明确了实现目标效果的关键,接下来只需按部就班设置相关样式即可。首先通过linear-gradient生成一张纯色图片作为元素的背景图片,并设置为不允许重复;其次设置background-size属性,设置初始宽高,使其看起来为一条线;然后通过background-position将背景定位在元素底部,变为下划线。此时查看页面样式,发现每行文本底部的下划线效果已经实现。 ⑥ 最后我们实现平滑动画效果。首先设置background-size属性,将初始宽度设置为0,将背景隐藏;然后在元素hover的时候,将背景宽度恢复为100%,显示背景;再然后借助transition属性,实现平滑的过渡效果,使得背景能逐行动态平滑的显示。 2、具体代码 HTML <div class="title-box"> <span> 有志者,事竟成。破釜沉舟,百二秦关终属楚。苦心人,天不负。卧薪尝胆,三千越甲可吞吴。有志者,事竟成。破釜沉舟,百二秦关终属楚。苦心人,天不负。卧薪尝胆,三千越甲可吞吴。 </span> </div>CSS .title-box { /* 父级盒子设置宽度 限制内部文本宽度 使其换行 */ width: 336px; margin: 50px auto; } .title-box span { color: #333; /* 通过渐变设置图片背景 且不允许重复 */ background: linear-gradient(to right, #333, #333) no-repeat; /* 背景初始宽度为0 高度为2px */ background-size: 0 2px; /* 背景位置在文字底部 */ background-position: left bottom; /* 设置平滑过渡效果 */ transition: background-size 1.5s ease-in-out; cursor: pointer; } /* hover时触发动画 */ .title-box span:hover { /* 背景宽度变为100% 高度不变 */ background-size: 100% 2px; /* 可以通过改变背景位置 改变下划线出现和消失的方向 */ /* background-position: right bottom; */ }三、相似扩展 1、效果1 图片 2、效果2 图片
-
揭开迷雾:全面解析 JS 中的 this 指向问题 一、简介 身为一个使用JavaScript的开发者,我们或多或少在开发中都遇到过因 this 指向不明导致的代码错误。函数中this 关键字的指向是一个复杂但又关键的概念,大多数时候它的指向不是在编写时确定的,而是在代码执行时根据函数的调用方式来决定,简单来说,this 的指向取决于函数的调用者(箭头函数除外)。 本文将全面详细的讲解this的指向规则,让你能够清晰理解并正确使用 this,避免由于错误的指向导致的程序异常,并提升对JavaScript的整体理解。 二、详解 1、 全局作用域 在全局作用域下(非严格模式),也就是最外层的JS脚本中,this指向全局对象(浏览器中为window对象)。 <script> console.log(this === window); // 浏览器中输出 true </script>2、普通函数 在调用一个独立的普通函数时(非严格模式),此时相当于通过全局对象来调用函数,this会进行默认绑定,指向全局对象(浏览器中为window对象)。 重点:普通函数中的 this 指向是动态的,取决于函数的调用方式,会因为调用方式的不同而发生改变。 // 非严格模式下 // 两个普通函数 function showThis() { console.log(this); } const showThis2 = function() { console.log(this); } // 调用普通话函数 showThis(); // 输出 window 对象 showThis2(); // 输出 window 对象 // 相当于通过window来调用函数 因此this指向window window.showThis(); // 输出 window window.showThis2(); // 输出 window如果让一个对象内部的方法指向普通函数,并通过该对象来调用函数,则this指向发生改变,指向调用函数的对象,谁进行调用,this就指向谁。 // 普通函数 function showThis() { console.log(this); } // 创建对象 并让对象的方法指向函数 const obj = { getThis: showThis }; // 通过对象调用函数 obj.getThis(); // 输出 obj3、箭头函数 箭头函数不会创建自己的this,其内部的this会继承函数定义时其外层最近的非箭头函数的 this 指向。如果是在全局作用域内使用箭头函数,则this指向全局对象;如果是在构造函数中使用箭头函数,则this指向new出来的新对象... 重点:箭头函数中的 this 指向是静态的,在函数定义时确定,后面不会因为调用方式的不同而发生改变,也无法被call/apply/bind等方法改变。 // 非严格模式下 // 箭头函数 this指向window const showThis3 = () => { console.log(this); } // 调用箭头函数 showThis3(); // 输出 window 对象如果让一个对象内部的方法指向箭头函数,并通过该对象来调用函数,此时虽然函数的调用者发生变化,但this指向不会改变,依旧指向全局对象。 // 箭头函数 const showThis3 = () => { console.log(this); } // 创建对象 并让对象的方法指向函数 const obj = { getThis: showThis3 }; // 通过对象调用函数 但this指向不会改变 obj.getThis(); // 输出 window4、调用对象的方法 在通过对象直接调用其内部的方法时,方法中的this 会进行隐式绑定,指向当前对象。 const obj = { getThis: function() { // 对象的方法中的this 指向当前对象 console.log(this) } }; // 通过对象调用方法 this指向当前对象 obj.getThis(); // 输出 obj如果先用一个变量指向对象的方法,然后再通过变量来调用对应的方法时,this的指向将不再指向对象,而是指向全局对象(浏览器中为window对象)。因为此时相当于进行独立函数调用,遵守独立函数的默认绑定规则。 <script> const obj = { getThis: function () { console.log(this) } }; // 通过对象调用方法 this指向对象本身 obj.getThis(); // 输出 obj // 使用变量指向对象的方法 但不调用 const fun = obj.getThis; // 通过变量调用方法 此时相当于独立函数调用 所以this指向window fun(); // 输出 window </script>如果使用一个对象的方法指向另一个对象的方法,此时两个对象都可以调用该方法,那么具体的this指向取决于调用者,谁进行调用, this就指向谁。 <script> const obj = { getThis: function () { console.log(this) } }; // 通过obj调用方法 this指向obj本身 obj.getName(); // 输出 obj // 在另一个对象中使用属性指向前面对象的方法 const obj2 = { // 指向obj的方法 get2: obj.getThis } // 通过obj2调用方法 此时this指向obj2 obj2.get2(); // 输出 obj2 </script>还存在一种特殊情况:如果对象的方法使用的是箭头函数,那么方法中的 this 将继承定义对象时所在作用域的this指向,而非对象本身,并且后续不会因为调用者改变而改变this指向。 <script> // 如果对象的方法使用的是箭头函数 那么this将继承定义对象时所在作用域的this指向 // 在全局作用域下定义对象 const obj = { getThis2: () => { // 此时箭头函数中的this 指向全局对象window console.log(this) } } // 虽然是通过obj调用的 但是其this还是会指向全局对象window obj.getThis2(); // 输出 window // 使用变量指向对象的方法 但不调用 const fun = obj.getThis2; // 通过变量调用方法 this依旧指向window fun(); // 输出 window // 在另一个对象中使用属性指向对象的方法 const obj2 = { // 指向obj的方法 get2: obj.getThis2 } // 通过obj2调用方法 此时this依旧指向window obj2.get2(); // 输出 window </script>5、调用构造函数 在使用new关键字调用构造函数时,会创建一个新的对象,此时this 会指向被创建的新对象。内部的普通函数和箭头函数内的 this 也都指向被创建的新对象,但后续普通函数的this指向是会根据调用者不同而发生改变,箭头函数则不会改变。 // 构造函数 function Person(name) { // 构造函数中的this指向通过new创建的新对象 this.name = name; // 普通函数 this.getThis = function () { console.log(this) } // 箭头函数 this.getThis2 = () => { console.log(this) } } // 调用构造函数创建新对象 const person = new Person('Bob'); // 通过新对象调用方法 此时this指向新对象 person.getThis(); // 输出 person person.getThis2(); // 输出 person // 在另一个对象中使用属性指向new出来的新对象的方法 const obj = { getThis: person.getThis, getThis2: person.getThis2 } // 通过obj调用方法 此时this指向会根据函数类型不同而不同 obj.getThis(); // 输出 obj obj.getThis2(); // 输出 person 还存在一种特殊情况,就是Function()构造函数,在使用该构造函数创建新函数时,其内部的this指向并非指向新函数本身,而是根据函数被调用的方式来决定this指向,谁调用this就指向谁。 // 非严格模式 // 在全局作用域下 const func = new Function('console.log(this)'); // 相当于通过window来调用函数 因此this指向window func(); // 输出 window 对象 // 在对象中 const p = { name: 'Alice', get: func } // 通过对象中调用函数 对象内的this指向对象本身 p.get(); // 输出 Person { name: 'Alice', get: [Function] }6、事件处理函数 在事件处理函数中,如果绑定的函数是普通函数,则相当于由DOM元素调用函数,则函数内的this指向触发事件的DOM元素;如果绑定的是箭头函数,则函数内的this会继承绑定事件处理函数所在作用域的this指向。 <button id="btn">Click me</button> <!-- 内联的事件处理器也一样 --> <button onclick="console.log(this)">Click me</button> <script> const btn = document.getElementById('btn'); // 绑定普通函数 相当于dom调用函数 所以this指向button元素 btn.addEventListener('click', function () { console.log('普通函数this--', this); // 输出 button 元素 }); // 绑定箭头函数 继承绑定事件处理函数所在作用域的this 此时为全局作用域 所以this指向window btn.addEventListener('click', () => { console.log('箭头函数this--', this); // 输出 window }); </script>7、回调函数 当把一个函数作为回调函数传递后,其this指向取决于回调函数的调用方式和函数的类型两者: 如果函数类型为普通函数且被传递后是直接独立调用的,此时相当于独立函数调用,函数内部this指向全局对象(浏览器中为window对象)。 如果函数类型为普通函数且被传递后是通过别的对象进行调用的,此时相当于通过对象调用函数,函数内部的this指向函数的调用者。 如果函数类型为箭头函数,则无论被传递后是如何进行调用的,都不会修改函数内部的this指向,箭头函数内部的this指向在函数定义后确定就不会再被改变。 const obj = { // 普通函数 showThis: function () { console.log(this); }, // 箭头函数 showThis2: () => { console.log(this); } } function fun(fn) { // 直接独立调用回调函数 fn(); } function fun2(fn) { // 通过对象调用回调函数 const a = { getThis: fn, } a.getThis(); } // 对象普通函数中的this指向对象本身 obj.showThis(); // 输出 obj // 对象箭头函数中的this继承定义对象时所在作用域的this指向 此时为window obj.showThis2(); // 输出 window // 传入一个普通函数 并且直接调用函数 相当于独立函数调用 所以this指向window fun(obj.showThis); // 输出 window // 传入一个箭头函数 并且直接调用函数 this指向不受影响 依旧为window fun(obj.showThis2); // 输出 window // 传入一个普通函数 并且通过对象调用函数 this被改变 指向其调用者 fun2(obj.showThis); // 输出调用者对象 a // 传入一个箭头函数 并且通过对象调用函数 this指向不受影响 依旧为window fun2(obj.showThis2); // 输出 window数组提供的内置方法中,除了传递回调函数之外,还可以通过第二个参数指定回调函数的this指向。如果未指定this的指向,则取决于函数的调用环境内的this指向。 // 创建对象 const obj = { multiplier: 2 }; // 创建数组 const numbers = [1, 2, 3]; // 使用数组提供的map方法 第一个参数是回调函数 第二个参数是指定回调函数的this指向 const doubled = numbers.map(function (number) { // 回调函数中的this指向obj 所以this.multiplier指向obj.multiplier 值为2 return number * this.multiplier; }, obj); // 最终运算结果为[2, 4, 6] console.log(doubled); // 未指定this指向 回调函数中的this指向window const doubled2 = numbers.map(function (number) { return this; }); // 最终运算结果为[window, window, window] console.log(doubled2);8、call/apply/bind 显式指定 call()、apply()、bind()三个方法都可以显式的指定函数的this指向,只是在一些具体细节上有所不同。例如:call() 和 apply() 方法会在改变函数内部this指向的同时,调用函数执行一次。而bind() 不会调用函数执行,仅改变函数的this指向;在向函数传递参数时,call() 和 bind() 传递的参数都是用逗号隔开的独立参数,而apply()是以数组的形式来传递参数。 // call方法 function greet(text) { console.log(`${text}, ${this.name}`); } const user = { name: 'Charlie' }; // 使函数的this指向user 且会调用执行一次 greet.call(user, 'Hello'); // 输出 'Hello, Charlie' // apply方法 function introduce(age, gender) { console.log(`Name: ${this.name}, Age: ${age}, Gender: ${gender}`); } const user2 = { name: 'Dana' }; // 使函数的this指向user 且会调用执行一次 introduce.apply(user2, [25, 'Female']); // 输出 'Name: Dana, Age: 25, Gender: Female' // bind方法 const module = { x: 42, getX: function () { return this.x; } }; // 此时指向对象的方法 但不调用执行 const retrieveX = module.getX; // 调用执行 此时的 this 指向window 因为相当于独立函数调用 console.log(retrieveX()); // 输出 undefined // 修改this指向 使函数的this指向module 且不会调用执行 const boundGetX = retrieveX.bind(module); // 手动调用执行一次 console.log(boundGetX()); // 输出 42三、课后练习 1、下面代码的执行结果是什么? // 全局作用域 且非严格模式 [1, 2, 3].forEach(function (item) { console.log(this); });2、下面代码的执行结果是什么? // 全局作用域 且非严格模式 const obj = { a: () => { console.log(this); }, b: function () { console.log(this); } }; obj.a(); obj.b();3、下面代码的执行结果是什么? // 全局作用域 且非严格模式 function Person(name) { this.name = name; this.getName = () => { return this.name; }; } const person1 = new Person('Alice'); const person2 = { name: 'Bob' }; person2.getName = person1.getName; console.log(person2.getName());欢迎大家在评论区揭晓答案
-
PHP8.1 推出了 fiber,fiber可以实现什么样的操作 ? PHP 8.1 引入了新的特性 fiber,也称为协程(Coroutines),它为 PHP 提供了一种轻量级的并发编程方式,使得开发者可以更方便地实现异步和并发操作。 Fiber 可以实现以下一些新的操作和用途: 轻量级线程:Fiber 允许在一个单独的 PHP 进程中创建多个轻量级线程,这些线程可以独立地执行不同的任务,实现并发处理,而不需要创建额外的进程或线程。 非阻塞 IO:通过使用 Fiber,可以实现非阻塞的 IO 操作,例如异步读写文件、网络请求等,从而提高 IO 操作的效率,避免程序在 IO 等待时被阻塞。 并发任务处理:Fiber 可以用于处理并发任务,例如在同时进行多个网络请求、数据库查询等任务时,可以使用 Fiber 来管理和协调这些任务的执行,提高处理效率。 轻量级同步:Fiber 可以用于实现轻量级的同步操作,例如在多个 Fiber 之间进行数据交换或共享数据,而不需要使用传统的锁机制。 嵌套调用:Fiber 可以进行嵌套调用,允许在一个 Fiber 中调用另一个 Fiber,从而实现更复杂的异步调用和逻辑。 异步编程:Fiber 可以与异步编程模式结合,例如配合事件循环库,实现更高效的异步编程。 通过引入 Fiber,PHP 可以在单个进程中实现高效的并发处理,而不需要依赖于多线程或多进程,从而减少资源开销和复杂性。这使得 PHP 能够更好地应对高并发和IO密集型的应用场景,例如网络编程、Web服务等。 以下是几个示例代码,展示了 Fiber 可以实现的一些新操作: 1 异步任务处理: 使用 Fiber 可以方便地实现异步任务处理,允许在一个单独的 PHP 进程中并发执行多个任务,提高处理效率。在下面的示例中,我们创建了两个 Fiber 来同时处理两个耗时任务,并在结束后输出结果。 <?php use Fiber\Fiber; function task1() { echo "Task 1 started.\n"; Fiber::suspend(); echo "Task 1 resumed.\n"; Fiber::suspend(); echo "Task 1 finished.\n"; } function task2() { echo "Task 2 started.\n"; Fiber::suspend(); echo "Task 2 resumed.\n"; Fiber::suspend(); echo "Task 2 finished.\n"; } $fiber1 = new Fiber('task1'); $fiber2 = new Fiber('task2'); $fiber1->start(); $fiber2->start(); $fiber1->resume(); $fiber2->resume(); $fiber1->resume(); $fiber2->resume(); ?>2 协程嵌套: Fiber 允许在一个 Fiber 中调用另一个 Fiber,实现协程的嵌套调用。在下面的示例中,我们创建了两个 Fiber,其中一个 Fiber 内部调用了另一个 Fiber。 <?php use Fiber\Fiber; function task1() { echo "Task 1 started.\n"; Fiber::suspend(); echo "Task 1 resumed.\n"; Fiber::suspend(); echo "Task 1 finished.\n"; } function task2() { echo "Task 2 started.\n"; $fiber = new Fiber('task1'); $fiber->start(); $fiber->resume(); echo "Task 2 resumed.\n"; $fiber->resume(); echo "Task 2 finished.\n"; } $fiber2 = new Fiber('task2'); $fiber2->start(); $fiber2->resume(); $fiber2->resume(); ?>3 非阻塞 IO: 使用 Fiber 可以实现非阻塞的 IO 操作,例如异步读写文件。在下面的示例中,我们使用 Fiber 来实现异步读取文件的操作。 <?php use Fiber\Fiber; function readFileAsync() { $file = fopen('example.txt', 'r'); echo "Start reading file.\n"; while (!feof($file)) { echo "Read: " . fgets($file); Fiber::suspend(); } fclose($file); echo "Finish reading file.\n"; } $fiber = new Fiber('readFileAsync'); $fiber->start(); while ($fiber->status() !== Fiber::STATUS_FINISHED) { echo "Resuming fiber...\n"; $fiber->resume(); } echo "Fiber finished.\n"; ?>在上述示例中,我们使用 Fiber 来异步读取文件,每次读取一行数据后就暂停 Fiber,等待下一次恢复。这样可以实现在文件读取过程中不阻塞其他操作的效果。 这些示例只是 Fiber 的一部分用法,实际上 Fiber 提供了更多灵活的协程编程方式,可以用于解决并发问题和异步编程等场景。由于 Fiber 是一个相对较新的特性,它在 PHP 中的使用需要根据具体需求和场景进行谨慎考虑。
-
一个简单而高效的论坛服务,几分钟之内构建属于自己的社区 flarum 一个简单而高效的论坛软件,不臃肿、不复杂、不过度依赖。几分钟之内构建属于自己的社区。旨在为您的网站构建出色的社区。图片 特点 快速且简单:Flarum 没有复杂的依赖关系,没有冗余功能,界面简洁。它使用PHP构建,易于部署,界面由Mithril这个轻量级的JavaScript框架驱动,性能出色。 美观且响应式:Flarum 被设计为跨平台一致且直观,用户体验友好,是为人类设计的论坛软件。 强大且可扩展:您可以自定义、扩展并集成Flarum以适应您的社区需求。Flarum的架构非常灵活,拥有强大的扩展API。 功能 社区平台:Flarum 不论社区大小,都能在各个层面上表现出色。通过扩展来增强Flarum核心体验,您可以根据需要定制您的社区。 发现扩展: Flarum 提供了丰富的扩展,让您可以调整Flarum以满足特定需求。 群组和权限:权限组允许您轻松配置权限和成员。您可以授予对类别的访问权限,同时定义全局和类别限制的操作,如版主管理、标记等。 强大的编辑器:Flarum 支持简单的BBCode/Markdown编辑器,也支持完整的所见即所得(WYSIWYG)编辑器,以满足不同用户的需求。 样式和主题:通过管理员外观设置页面轻松自定义,安装主题或使用自定义CSS完全修改外观,CSS会自动集成到所有页面和扩展中。创意无限! 通知和参与:使用浏览器内通知、电子邮件或各种其他渠道提高用户参与度。安装WebSocket扩展可以让您社区的互动性更强,实现实时更新。 无限扩展:以上只是Flarum提供的一部分功能,有数百个扩展可供选择,您可以根据需要调整Flarum!如果您需要一些完全独特或前所未有的功能,您可以轻松扩展或委托扩展Flarum。 源码地址 隐藏内容,请前往内页查看详情