首页
Search
1
解决visual studio code (vscode)安装时没有选择安装路径问题
320 阅读
2
如何在 Clash for Windows 上配置服务
215 阅读
3
Linux 下 Bash 脚本 bad interpreter 报错的解决方法
150 阅读
4
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
149 阅读
5
uniapp打包app提示通讯录权限问题,如何取消通讯录权限
113 阅读
clash
服务器
javascript
全部
游戏资讯
登录
Search
加速器之家
累计撰写
1,195
篇文章
累计收到
0
条评论
首页
栏目
clash
服务器
javascript
全部
游戏资讯
页面
搜索到
758
篇与
的结果
2024-10-21
使用node更新google hosts
现在网上有不少提供 google hosts 的网址(本文的最后提供了几个 google hosts 的网站地址),当我们访问不了 google 时,就需要复制这个网站提供的 hosts 到我们本地的 hosts,在粘贴前还要在 C 盘一层一层的找到本地 hosts,比较麻烦。于是就想能不能通过运行一条命令就能把网站里的 hosts 更新到本地,刚开始想的是用 php 写的,不过在跟同学聊天的过程中,同学建议使用 node 来实现这个功能,简单方便,而且我对 php 的正则表达式也不是很熟,那就用 node 试试吧。我目前的环境是 windows,接下来的步骤大部分是按照 windows 操作系统进行操作的: 安装 node; 下载需要的集成包; 编写程序; 运行 1. 安装 node # 在官方的【下载链接】中,下载我们需要的链接,我选择的是下面这个类型。下载后,双击进行安装。在现在的 node 版本中,默认是 node 和 npm 一起安装的,不用再单独安装 npm 了。安装成功后,使用 win+r 打开 cmd 命令窗口,然后输入node -v查看当前 node 的版本号输入npm -v查看当前 npm 的版本号如果两个都能正确的输出版本号,就说明 node 和 npm 已经安装成功了。使用.mis类型的文件进行能够自动添加 node 和 npm 到 PATH 的环境变量中,让我们在任何的目录下都能使用这两个命令。 2. 下载需要的集成包 # npm 是一个 node 的包管理和分发工具,已经成为了非官方的发布 node 模块(包)的标准。有了 npm,可以很快的找到特定服务要使用的包,进行下载、安装以及管理已经安装的包。使用 npm 进行模块的安装非常的方便:npm install ****,如果想安装成全局的,所有的项目都能使用,可以添加上-g参数,如:npm install -g ****。本项目中主要使用到的模块有: request : 远程 url 请求 cheerio : 类似于 jQuery,能够像 jQuery 那样选择元素,获取元素的内容 fs : 这是 npm 里自带的,不用下载安装,主要用于文件的读写操作 3. 编写程序 # 程序的逻辑很简单: 获取远程 url 的网页内容; 解析出网页内容中 hosts 部分; 读取自己设置的一些 hosts; 讲自己的 hosts 和远程获取到的 hosts 一起写入到 C 盘的 hosts 文件中。 这里使用了 async 中的 waterfall(瀑布流)来控制整个代码的流程。async.waterfall( [ function (callback) { self.log("正在连接 hosts url...."); callback(error, data); }, function (body, callback) { self.log("正在解析数据...."); callback(null, text); }, function (text, callback) { self.log("读取本地文件...."); callback(err, data); }, function (text, callback) { self.log("正在写入hosts...."); if (err) { self.log("写入失败...."); } else { self.log("写入成功...."); } calllback(err); }, function () { self.log("运行完毕!"); }, ], function (err, data) { console.log("err: " + err); // console.log('data: '+data); } ); async.waterfall 提供瀑布式的流控制。每个操作产生的数据可以传递给下一个函数,通过 callback 这个回调函数。async 模块保证回调只会被触发一次。它同时还为我们处理了错误,管理并行的任务。这里还要说明一下:我这儿有一个单独的文件来放置自己的 hosts,比如当前目录中的 default.txt 文件完整的代码可在github node-hosts下载 4. 运行代码 # 进入到这个项目所在的目录,首先运行npm install,安装程序所需要的集成包。安装完成之后,然后运行 index.js:node index.js。当然,这里还有一个问题要注意,hosts 文件需要有可读写的权限,不然 hosts 是无法进行修改的。程序采用的更新 hosts 的方式是全覆盖写入,因此我们需要把自己配置的一些 hosts 写到一个文件里(如 default.txt)。当程序更新时,会首先读取 default.txt 里的 hosts 配置,然后与远程地址里的 google hosts 一起写入到本地的 hosts 文件中。代码里还有参数的默认配置:_option : { hostsurl : 'http://www.360kb.com/kb/2_122.html', // 请求地址 hostsfile : 'C:/Windows/System32/drivers/etc/hosts',// 本地hosts地址 localfile : './default.txt' // 默认hosts } 你可以直接修改这些变量,或者在调用 init()方法时传递你需要的参数,程序自然会覆盖掉默认参数:wzHosts.init({ hostsfile: "/etc/hosts", localfile: "/data/default.txt" }); 这样,本地里的 google hosts 就更新到了最新! 5. 总结 # 通过这次的 hosts 读写,学习到了不少的 node 知识,欢迎提出你们的意见和建议!
2024年10月21日
7 阅读
0 评论
0 点赞
2024-10-21
node-socket实现web的即时聊天系统
本文将使用 Node.js 加 web socket 协议打造一个网页即时聊天程序,其中将会使用到 express 和 socket.io 两个包模块,下面会有介绍。首先放出代码链接【github-wchat】 1. node 的安装与使用 # 关于如何安装 node,可以参考我的上篇文章【使用 node 更新 google hosts】,那篇文章比较详细的介绍了如何安装 node。 2. 聊天系统使用到的模块 # node 的火爆得益于其丰富的模块管理系统(npm),我们能够从这里获取到任何我们想要的模块,同时我们也能上传自己的模块。关于这个聊天项目,我们使用到了两个模块:express 和 socket.io,使用 npm 安装这两个模块即可npm install express npm install socket.io 安装成功之后,就需要使用 express 构建我们的 web 开发框架var express = require("express"), //引入express模块 app = express(), server = require("http").createServer(app), io = require("socket.io").listen(server); app.use("/", express.static(__dirname + "/www")); //指定静态HTML文件的位置 server.listen(80); 这样,我们把所有的前端文件放到 www 目录里就行,比如 html, css, js 和 images 等。更多关于 express 的信息,可以参考这里:express api 3. 简要介绍 node 的事件机制 # 其实在 node 中关于事件机制有很多的内容,这里主要讲解 emit 和 on 的使用:emit 是事件产生器,用来产生事件的,on 是事件接收器,用来接收事件的,那么这两个正好组成了一组。一个在前端产生,另一个在后端接收,或者相反。这样事件就能在前后端进行传递了,同时也能进行参数的传递。前端:// wchat.js socket.emit("login", { nickname: nickname, img: $img.attr("src") }); 后端:// server.js socket.on("login", function (obj) { if (User.hasExisted(obj.nickname)) { socket.emit("nickExisted"); } else { obj.uid = socket.id; User.info[socket.id] = obj; // 通知用户登录成功 socket.emit("loginSuccess", obj.uid); var s = { nickname: obj.nickname, user: User.info, len: User.getLength(), type: "login", }; // 通知其他人有新用户进入 io.sockets.emit("system", s); } }); 4. web 即时聊天的思路 # 4.1 用户的登录与退出: # 因为暂时没有使用数据库,用户只能临时输入用户名,只要跟之前的用户不重复就能使用。同时在本系统中,还增加了一个可选择自己头像的功能,然后与用户名一起提交到 server.js,server.js 中有一个数组专门来存储所有登陆的用户。后续会添加上数据库的功能,来验证用户,不必再使用数组保存用户。// 登陆 login : function(){ var nickname = $("#nickname").val(); if(nickname===""){ return; } var $img = $(".portrait .selected").find("img"); // 获取头像地址 socket.emit("login", {nickname:nickname, img:$img.attr("src")}); return false; } server.js 收到用户的登录信息后,先判断用户名是否重复,如果没有重复,则返回登陆成功的信息,同时提示其他用户有新用户加入,并更新在线用户列表。新用户验证成功后,在wchat 中的 userid 字段中保存从服务器返回的用户编号,获取所有的用户列表,然后就可以聊天了。socket.on("login", function (obj) { if (User.hasExisted(obj.nickname)) { socket.emit("nickExisted"); } else { obj.uid = socket.id; User.info[socket.id] = obj; socket.emit("loginSuccess", obj.uid); // 更新在线用户列表 var s = { nickname: obj.nickname, user: User.info, len: User.getLength(), type: "login", }; io.sockets.emit("system", s); console.log(obj.nickname + " 已接入"); } }); 4.2 用户发送信息给其他用户 # 用户登录成功后就可以发送消息了:// user_msg是用户要发送的消息 socket.emit("message", {msg:user_msg}, userid); 当然这之前还需要进行一些其他的检验,比如判断 wchat.userid 是否为空,若为空表示用户还没有登陆,不能进行发送;若输入框内没有内容,直接点击发送按钮,也不能向服务器发送请求。当所有的验证都通过了,就可以发送请求了。server.js 接收到message 发送过来的请求后,首先验证是否有 userid 这个用户,如果没有则返回提示“该请求非法”,否则就使用msg向所有人发出这个消息,这个消息里具体的字段有:{ "status": "success", "info": { "userid": "123", "nickname": "wenzi", "image": "xx.png", "msg": "hello, my name is wenzi" } } 前端接收到 msg 的请求后,判断 status 的值是否等于 success,若等于,则解析 info 字段,将这些信息追加到输入框中:if (result.status == "success") { var info = result.info; var $content = $(this.content); this.userid == info.uid && $content.addClass("louzhu"); $content.find("img").attr("src", info.img); $content.find(".name").html(info.nickname); $content.find(".timer").html(info.time); $content.find(".msg").html(info.msg); $(".record").find(".list").append($content); this.scroll(); // 保证滚动条在最底部 $("#msg").val("").focus(); } else { this.warning("认证失败"); } 到这里,最基本的聊天功能已经完成了。我们还以给这个聊天系统弄一些锦上添花的功能,比如可以修改文字的颜色和大小,发送图片等功能。 4.3 修改文字的颜色和大小 # 在 html 中插入颜色选择器和文字大小修改控件,让用户进行选择。 颜色选择器可以使用 html5 中提供的 input:type=color 标签,文字大小修改控件我采用的是 select 标签让用户进行选择。 12px 14px 16px 18px 20px 22px 当然用户也可以不选择,这两个都有默认值,文字颜色默认是#000000 ,文字大小默认是14px ,每次发送消息时,都获取一次这两个控件的值:socket.emit( "message", { msg: msg, color: $("#fontcolor").val(), size: $("#fontsize").val() }, userid ); 服务器接收到这些值后,可以先拼接好 html 后再返回到前端,或者直接把这些属性发送到前端,让前端进行拼接。 4.4 发送图片 # 上面已经实现了基本的聊天功能了,进一步,如果我们还想让用户可以发送图片,那程序便更加完美了。图片不同于文字,但通过将图片转化为字符串形式后,便可以像发送普通文本消息一样发送图片了,只是在显示的时候将它还原为图片。在这之前,我们已经将图片按钮在页面放好了,其实是一个文件类型的 input,下面只需在它身上做功夫便可。用户点击图片按钮后,弹出文件选择窗口供用户选择图片。之后我们可以在 JavaScript 代码中使用FileReader来将图片读取为 base64 格式的字符串形式进行发送。而 base64 格式的图片直接可以指定为图片的 src,这样就可以将图片用 img 标签显示在页面了。为此我们监听图片按钮的 change 事件,一但用户选择了图片,便显示到自己的屏幕上同时读取为文本发送到服务器。$("#fileupload").on("change", function () { //检查是否有文件被选中 var $this = $(this), files = $this[0].files; if (files.length != 0) { //获取文件并用FileReader进行读取 var file = files[0], reader = new FileReader(); if (!reader) { Chat.warning("您的浏览器不支持FileReader"); $this.val(""); return; } reader.onload = function (e) { //读取成功,显示到页面并发送到服务器 $this.val(""); var $img = $(''), img = $img[0]; socket.emit( "message", { msg: '', color: $("#fontcolor").val(), size: $("#fontsize").val(), }, Chat.userid ); // that._displayImage('me', e.target.result); }; reader.readAsDataURL(file); } }); 我们使用上面发送文字的事件来发送图片,然后再通过服务器分发到各个用户的聊天面板上。有时候我们发送会很大的图片,产生横向的滚动条,破坏掉布局,因此我们限制图片的高度:.chat .info_msg .msg img { max-width: 174px; } 之前不知道怎么实现点击查看原图,后来才发现,能够根据e.target.result构造出一个 jQuery 对象:$img,然后顺便得出 DOM 对象:var img = $img[0]。通过这个 img 能够获取图片原生的高度和宽度,然后将这个高度和宽度作为属性存储到 img 标签中。用户点击被等比例缩小的图片时,获取这个存储的宽度和高度,展示出来。 5. 总结 # 到这里,基本上所有的东西已经完成了,当然,还有很多改进的地方,会在以后慢慢补上。比如有:发送语音,添加数据库,使用 CSS3 进行窗口抖动,可以一对一聊天等。其实一对一聊天已经实现了个大概了,在shaobings_one 的分支上,只是目前只实现了前端的功能,还不能真正的发送消息。如果你有什么建议和意见,欢迎留言。
2024年10月21日
14 阅读
0 评论
0 点赞
2024-10-21
jQuery 的 promise
为了让前端们从回调的地狱中回到天堂,jQuery 也引入了 Promise 的概念。Promise 是一种令代码异步行为更加优雅的抽象,有了它,我们就可以像写同步代码一样去写异步代码。jQuery 从 1.5 版本开始实现了 CommonJS Promise/A 规范这一重量级方案,不过没有严格按照规范进行实现,有一些 API 上的差异。 1. 以前的 ajax # 1.1 原生的 ajax 写法 # 以前我们写 ajax 请求时,如果是用原生的 JavaScript 代码写呢,通常是这样的一种形式:var XHR = (function () { var obj = null; // xhr对象 function init() { create(); send(); } // 创建xhr对象 function create() { if (window.XMLHttpRequest) { obj = new XMLHttpRequest(); } else if (window.ActiveObject) { obj = new ActiveObject("Microsoft.XMLHTTP"); } echange(); } // 发送消息 function send() { var v = parseInt(Math.random() * 100); obj.open("get", "test.php?v=" + v, true); obj.send(null); obj.onreadystatechange = echange; } // 接收消息 function echange() { /* 0 (未初始化) 对象已建立,但是尚未初始化(尚未调用open方法) 1 (初始化) 对象已建立,尚未调用send方法 2 (发送数据) send方法已调用,但是当前的状态及http头未知 3 (数据传送中) 已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误 4 (完成) 数据接收完毕,此时可以通过通过responseBody和responseText获取完整的回应数据 */ if (obj.readyState == 4 && obj.status == 200) { var result = JSON.parse(obj.responseText); console.log(result); } } return { init: init, }; })(); 1.2 jQuery 的 ajax 写法 # 原生的 ajax 代码能比较清晰的看出 ajax 的流程,可是如果每次都写这么多的代码就会造成很多的不便。不过在 jQuery 中,已经对 ajax 的代码进行封装,使用 jQuery 的 ajax 时就不用写这么多的代码了。当然,我们自己也可以对原生的 ajax 代码进行封装。$.ajax({ url: "test.php", // 请求地址 data: { v: v }, // 发送的数据 dataType: "json", // 返回的数据格式 type: "get", // 请求的方式:get|post // 发送前执行 beforeSend: function () { console.log("beforeSend"); }, // 返回成功执行 success: function (result) { console.log(result); }, // 返回失败执行 error: function (err) { console.log(err); }, // 无论成功还是失败都会执行 complete: function () { console.log("complete"); }, // 状态码对应的操作 statusCode: { 200: function () { console.log("200, ok"); }, 404: function () { console.log("404, page not found"); }, }, }); 上面的代码中虽然列了很多的选项,但是不是所有的都能用到的,可以结合实际的例子进行相应的取舍。比如 beforeSend, statusCode 等,如果实在是用不到,也可以不写的。 1.3 多个 ajax 顺序请求时 # 在使用这样的 ajax 进行多次顺序请求时,我们会采用嵌套的方式来进行。// 首次ajax请求 $.ajax({ url: "test.php", success: function () { // 请求完成后进行下次的请求 $.ajax({ url: "test1.php", success: function () { // 进行下次的ajax请求 $.ajax({ url: "test.php", success: function () { // ... }, }); }, }); }, }); 如果有比较多的 ajax 请求时,我们发现这样的代码实在是不利于我们的阅读,而且造成以后代码的修改和维护很困难。那有没有一种写法,我们既能分开写 ajax 请求,又能顺序执行。当然有啦,一个比较简单的方式就是使用异步回调的方式:// 第一个ajax请求 function ajax(callback) { $.ajax({ url: "test.php", success: function () { callback(); }, }); } // 第二个ajax请求 function ajax1(callback) { $.ajax({ url: "test.php", success: function () { callback(); }, }); } // 将第二个ajax方法作为第一个方法的回调方法 // 如果有多个ajax请求时,可以调用多个回调方法 ajax(ajax1); // ajax(ajax1(ajax2)); 2. 现在的 ajax # Promise/Deferred 模式在 JavaScript 框架中最早出现于 Dojo 的代码中,被广为所知则来自于 jQuery 1.5 版本,该版本几乎重写了 ajax 部分,使得调用 ajax 时可以通过如下的形式进行:$.ajax({ url: "test.php" }) .success(function () {}) .error(function () {}) .complete(function () {}); 这使得即使不调用 success(), error()等方法,ajax 也会执行,这样的调用方式比预先传入的回调让人觉得舒适一些。在原始的 API 中,一个事件只能处理一个回调,而通过 Deferred 对象,可以对事件加入任意的业务处理逻辑,示例代码如下:$.ajax({ url: "test.php" }) .success(function () {}) .error(function () {}) .success(function () {}); Promise/Deferred 模式在 2009 年时被 Kris Zyp 抽象为一个提议草案,发布在 CommonJS 规范中,随着使用 Promise/Deferred 模式的应用逐渐增多,CommonJS 草案目前已经抽象出了 Promise/A、 Promise/B、Promise/D 这样典型的异步 Promise/Deferred 模型,这使得异步操作可以以一种优雅的方式出现。jQuery 的 1.5 版本之后,$.ajax()返回的就是 promise 对象,而且其他的 jQuery 对象也能通过调用 promise()返回一个 promise 对象。因此,上面的 ajax 可以写成这样:var promise = $.ajax({ url: "test.php" }); promise.success(function () { // success }); promise.error(function () { // error }); 不过在 1.8 版本之后,jQuery 已经不推荐 success(),error()和 complete()方法了,推荐使用 done(), fail()和 always(),分别对应着前面三个方法。修改如下:var promise = $.ajax({ url: "test.php" }); promise.done(function () { // done }); promise.fail(function () { // fail }); promise.always(function () { // always }); 我们也可以用 then()方法把 done()和 fail()合并到一起。promise.then( function () { // done }, function () { // fail } ); 第一个参数表示 done 方法,第二个方法表示 fail 方法;如果只传递一个参数的话,就表示 done 方法。同时在 jQuery 中还提供了$.when()方法 $.when(deferreds) : 提供一种方法来执行一个或多个对象的回调函数, Deferred(延迟)对象通常表示异步事件 如果向 jQuery.when() 传入一个延迟对象,那么会返回它的 Promise 对象(延迟方法的一个子集)。可以继续绑定 Promise 对象的其它方法,例如, defered.then 。当延迟对象已经被受理(resolved)或被拒绝(rejected)(通常是由创建延迟对象的最初代码执行的),那么就会调用适当的回调函数。$.when($.ajax({ url: "test.php" })).then(function (data, status, jqXHR) { // done // console.log(data, status, jqXHR); }); 在案例中有多个延迟对象传递给 jQuery.when() ,该方法返回一个新的“宿主”延迟对象,跟踪所有已通过 Deferreds 聚集状态。 当所有的延迟对象被受理(resolve)时,该方法才会受理它的 master 延迟对象。当其中有一个延迟对象被拒绝(rejected)时,该方法就会拒绝它的 master 延迟对象。如果 master 延迟对象被受理(resolved),那么会传入所有延迟对象的受理(resolved)值,这些延迟对象指的就是传给 jQuery.when 的参数。例如,当延迟对象是 jQuery.ajax() 请求,那么传入的受理(resolved)参数就是请求用的 jqXHR 对象,传入顺序就是它们在参数列表中的顺序。在多延迟情况下,如果延迟一被拒绝,jQuery.when 触发立即调用 master 延迟对象的 failCallbacks。请注意,在上述情况中,有一些延迟对象依然是不能被受理(unresolved)的。那么,在这种情况下,如果需要执行一些额外的处理,例如,取消所有未完成的 ajax 请求,你可以在闭包中进行保持 jqXHR 对象的引用,并且在 failCallback 中检查或取消它们。$.when($.ajax({ url: "test.php" }), $.ajax({ url: "test1.php" })) .done(function (result, result1) { console.log(result); console.log(result1); }) .fail(function (err) { console.log("error"); }); 当两个 ajax 都执行成功时就会调用 done 方法,否则只要其中一个失败就会执行 fail 方法。 3. promise 对象使用的其他场景 # promise 对象不只是能在 ajax 中使用,在其他有时间延迟的地方也能够使用,比如animate(), fadeIn(), fadeOut() 等,我们想在 fadeIn()之后执行 animate(),然后执行 fadeOut 让他隐藏。一种方法就是使用这个方法提供的回调函数,来确保它的先后顺序:.process { background: #ccc; display: none; width: 150px; height: 150px; } // 记得加载jQuery库 $(".process").fadeIn(function () { $(this).animate({ width: "300px", height: "300px" }, 1000, function () { $(this).fadeOut(); }); }); 还有就是用上面讲到的 promise 对象。$(".process") .fadeIn() .promise() .then(function () { $(this).animate({ width: "300px", height: "300px" }, 1000); }) .then(function () { $(this).fadeOut(); });
2024年10月21日
8 阅读
0 评论
0 点赞
2024-10-21
leetcode-rotate-array
这是leetcode的一道算法题目,主要是讲解数组的旋转。【189-Rotate Array】 Rotate an array of n elements to the right by k steps.For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4].Note:Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem. 题目的主要是意思是:向右旋转一个有n元素的数组k个步骤;比如一个数组的长度n=7, 移动的步数k=3,数组[1, 2, 3, 4, 5, 6, 7],旋转之后的数组为[5,6,7,1,2,3,4]。从题目中我们能够知道,其实我们没有必要移动k个步骤,移动k%n个步骤就行了,因为中间的一些步骤都是重复循环。我们只需要移动最后一个循环中剩余的几步即可完成,这样就能减少很多的步骤和操作。leetcode能够选择多个编程进行回答,而我选择的是JavaScript,不过使用JavaScript进行编程时,里面有个提示非常的重要:/** * @param {number[]} nums * @param {number} k * @return {void} Do not return anything, modify nums in-place instead. */ 如果用JavaScript的话,那么你的程序必须是:不用返回任何数据,而且,只能修改nums,不能替换nums的数据,比如:nums=***。刚开始没有注意到这句话,就想着,把最后k个数获取到,然后跟剩余的拼接起来,并赋值给nums即可。于是就产生了下面的程序:var rotate = function(nums, k) { var len, after; len = nums.length; k = k%len; // 对k进行处理 after = nums.splice(-k, k); // 截取最后k个元素 nums = after.concat(nums); // 最后k个元素与前面剩余的元素拼接起来 }; 用这个程序竟然没有通过,后来才注意到这个提示。我们只能对nums数组进行操作,即我们必须使用一些能够改变数组本身的方法,比如push(), pop, shift(), unshift(), splice()等;而slice(), concat()等这些方法是不能使用的,因为它们只是返回了我们需要的数据,但是原数组并没有发生改变。现在让我们回忆一下这些方法的使用: push(elem0, elem1, elem2, ...) 向数组的末尾添加元素,并返回新的长度 pop() 删除数组中最后的一个元素,同时返回该元素 shift() 把数组的第一个元素从其中删除,并返回第一个元素的值 unshift(elem0, elem1, elem2, ...) 向数组的开头添加一个或更多元素,并返回新的长度 splice(start, len, elem0, elem1, elem2, ...) 从数组的start位置删除len个元素,同时在该位置填充elem0, elem1等元素。如果elem为空,则直接进行删除,同时返回被删除的元素(array) slice(start, len) 从数组的start位置删除len个元素,同时返回被删除的元素,而原数组不变 concat(array) 在数组的后面接上一个数组array,同时返回拼接完成的数组,而原数组不变 从上面的各个方法中,我们能够看到,只能使用前面5个方法,最后2个方法不能修改原数组。因此现在的思路修改为:使用splice()得到最后的k个元素,然后使用unshift()把得到的数据一个个填充到数组的前面/** * @param {number[]} nums * @param {number} k * @return {void} Do not return anything, modify nums in-place instead. */ var rotate = function(nums, k) { var len, after; len = nums.length; k = k%len; after = nums.splice(-k, k); // 得到最后k个元素 for(var i=k-1; i>=0 ;i--){ nums.unshift(after[i]); // 填充到数组的开始位置 } };
2024年10月21日
6 阅读
0 评论
0 点赞
2024-10-21
二谈javascript中的定时器
在以前的文章【javascript 中的定时器】中,简单的介绍了一下 setTimeout()和 setInterval()两个定时器方法的使用和原理。不过在昨天给我的 node 即时聊天系统添加消息提示时,发现了定时器新的特性。当然,这对于我来说是新的发现,其实这些东西早就已经存在了。 1. 最小运行时间间隔 # 在 setTimeout()和 setInterval()我们能够设定时间间隔,来让下个事件大致发生在哪个时间段。假如我们设置时间间隔是 0 的话,那是不是就会在 0ms 之后执行呢,也就是立即执行。我们可以采用下面的代码输出一下:function get() { var timer = null; var date = null; var diff = 0; var last = 0; var now = 0; var nums = 0; var color = "#000"; var process = document.getElementById("process"); timer = setInterval(function () { date = new Date(); now = date.getTime(); diff = now - last; // 前后两个时间差 last = now; nums++; color = diff === 0 ? "#f00" : "#000"; process.innerHTML += "" + now + ' (' + diff + ")"; if (nums >= 100) { clearInterval(timer); } }, 0); } get(); 我们把每次执行 setInterval()前后的时间差打印到屏幕中(以下数据使用 chrome 42.0.2311.90 版本测试):1429545782409 (1) 1429545782412 (3) 1429545782414 (2) 1429545782420 (6) 1429545782425 (5) 1429545782430 (5) 1429545782437 (7) 1429545782443 (6) 1429545782449 (6) 1429545782454 (5) 1429545782460 (6) 1429545782466 (6) 1429545782471 (5) 1429545782476 (5) ... 从打印出的数据可以看出,setInterval()的时间间隔为 0ms 时,输出的时间差基本都在 1~10ms 之间,也是能在可以接受的范围内。IE11 下的测试与 chrome 的数据基本一致,而在 firefox 下能够出现0的时间差。 2. 标签不可见时的定时器间隔 # 其实不管是把时间间隔设定为 0ms 还是其他的时间间隔,运行时都会有时间误差的,比设定的间隔多 1~16ms 毫秒左右,有的时候还会相差更多。我们有时会在某个场合对标题进行闪动,提示给用户当前标签页有新消息产生:var backup = document.title; // 存储原标题 function blink() { document.title = document.title == backup ? "【有新消息】" : backup; } blink(); timer = setInterval(blink, 500); 上面的代码能够进行 500ms 的标题轮流闪动,当我们处在当前标签页时,基本感觉不出定时器产生的误差。可是如果我们切换到其他的标签页或者最小化时,我们就能够看到,标题的闪动变慢了很多,差不多提升到 1000ms 左右了。为了更加准确的记录时间间隔的变化,我们特此将上面的代码进行如下的补充,标题进行闪动时记录当前的毫秒时间戳,同时标记出当前标签页可见时的状态和不可见时的状态【查看演示】:// 标题闪动 function blinkTile(title, timeout) { var self = this; var timer = null; var backup = document.title; var last = 0; var process = document.getElementById("process"); self.init = function (title, timeou) { if (title != undefined) { self.title = title; } self.timeout = timeout == undefined ? 500 : timeout; }; self.start = function () { self.stop(); function blink() { document.title = document.title == backup ? self.title : backup; self.check(); } blink(); timer = setInterval(blink, self.timeout); }; self.stop = function () { if (timer != null) { document.title = backup; clearInterval(timer); timer = null; } }; // 打印时间差,同时让滚动条在最下边 self.check = function () { var date = new Date(); var now = date.getTime(); var diff = now - last; last = now; process.innerHTML += "" + now + " (" + diff + ")"; process.scrollTop = process.scrollHeight; }; self.init(title, timeout); } var blink = new blinkTile("【新消息】", 500); blink.start(); // 标签页的可见状态 var hidden, state, visibilityChange; if (typeof document.hidden !== "undefined") { hidden = "hidden"; visibilityChange = "visibilitychange"; state = "visibilityState"; } else if (typeof document.mozHidden !== "undefined") { hidden = "mozHidden"; visibilityChange = "mozvisibilitychange"; state = "mozVisibilityState"; } else if (typeof document.msHidden !== "undefined") { hidden = "msHidden"; visibilityChange = "msvisibilitychange"; state = "msVisibilityState"; } else if (typeof document.webkitHidden !== "undefined") { hidden = "webkitHidden"; visibilityChange = "webkitvisibilitychange"; state = "webkitVisibilityState"; } var process = document.getElementById("process"); // 添加监听器,在title里显示状态变化 document.addEventListener( visibilityChange, function () { if (document[state] == "hidden") { process.innerHTML += '====== 离开 ======'; } else { process.innerHTML += '++++++ 回来 ++++++'; } }, false ); 运行后,我们能够看到程序记录下的数据有(以下仅是部分数据):1429547223336 (505) 1429547223837 (501) ====== 离开 ====== 1429547225296 (1459) 1429547226296 (1000) 1429547227296 (1000) 1429547228297 (1001) ++++++ 回来 ++++++ 1429547229137 (840) 1429547229637 (500) 我们很清楚的看到,当标签页不可见时(“离开”后),时间差上升了 1000ms 左右;标签页可见时(“回来”后),时间差又恢复到了 500ms 左右。不过在标签页刚切换完的时候,时间差的变化比较大,后来就趋于稳定了。其实浏览器为了在标签页不可见时减少 CPU 的利用率和电池等的消耗,特地将时间间隔进行提高。不过这里要指出的是,在 IE11 下,标签的可见状态不会影响定时器的时间间隔。 3. 如何解决时间间隔会变化的问题(2016 年 1 月 14 日更新) # 在上面的章节中,介绍了setInterval和setTimeout的时间间隔会随着标签页的可见性发生变化。但是在有的情形下,是不希望时间间隔发生变化的,那该如何解决呢。受下面评论的启发,使用web worker可以解决这个这个问题,但是因为 web worker 是 html5 里的标准,低版本的浏览器是支持不了的。当前页面:// 创建一个worker实例 var worker = new Worker("worker.js"); // 向worker.js发送信息 worker.postMessage("hello world"); var last = 0; // 接收从worker.js发送的信息,存储在event.data中 worker.onmessage = function (event) { var diff = event.data - last; last = event.data; $("#content").append(diff + ""); }; // 报错信息 worker.onerror = function (error) { console.log(error.filename, error.lineno, error.message); }; worker.js:// 接收前端页面发送过来的信息,存储在event.data中 onmessage = function (event) { var data = event.data; setInterval(function () { // 向前端页面发送信息 postMessage(Date.now()); }, 500); }; 在上面的例子中,可以看到,当前页面中的 js 和 worker.js 都是是通过postMessage和onmessage进行互相通信的。当我们执行这些代码时,无论当前页面是否可见,setInterval 每次的间隔都是**500**。因此,我们可以在worker.onmessage中进行相应的 DOM 操作。
2024年10月21日
12 阅读
0 评论
0 点赞
1
...
37
38
39
...
152