有个公司需求是在前端页面,点击按钮能成功跳到公司的APP。跟原生同事联调时候,记得让他们把URL scheme://告诉你。下面的脚本是通过jquery方式去判断当前手机是android还是ios,展示对应的跳转按钮;然后监听按钮的点击事件,通过iframe的方式试图打开APP,如果能正常打开,会直接切换到APP,并自动阻止a标签的默认行为。否则会alert提示用户下载。
脚本部分:
1 | <body> |
有个公司需求是在前端页面,点击按钮能成功跳到公司的APP。跟原生同事联调时候,记得让他们把URL scheme://告诉你。下面的脚本是通过jquery方式去判断当前手机是android还是ios,展示对应的跳转按钮;然后监听按钮的点击事件,通过iframe的方式试图打开APP,如果能正常打开,会直接切换到APP,并自动阻止a标签的默认行为。否则会alert提示用户下载。
1 | <body> |
在前端面试和日常业务中,Promise.race 是一个非常常见但又容易被忽略的 API。它的规则很简单:
多个 Promise 同时“赛跑”,谁先落地(fulfilled 或 rejected),整体 Promise 就跟着落地。
这篇文章我们会:
Promise.race 的行为与典型使用场景myPromiseRacePromise.race(iterable) 接收一个可迭代对象(通常是数组),返回一个新的 Promise:
比如请求接口超过 3 秒就当作超时处理:
fetch(url) vs timeoutPromise(3000)比如同一个资源从 CDN1/CDN2 同时拉取,谁先成功就用谁。
首屏数据、降级数据、缓存数据同时读取,谁先返回就先渲染。
你这版实现非常接近原生行为:
Promise.resolve 兼容非 Promise 值1 | function myPromiseRace(promises) { |
Promise.race 的参数里不一定都是 Promise,可能是:
Promise.race([1, 2, 3]){ then(resolve) { resolve(123) } }使用 Promise.resolve(x) 的好处是:
这也是符合原生 Promise.race 的行为。
原生
Promise.race([])会返回一个永远 pending 的 Promise。
你现在的实现遇到空数组也会返回 pending(因为 for 循环不执行),这符合原生。
如果你想更明确一点,可以加个计数,但不是必须。
1 | const p1 = new Promise(res => setTimeout(() => res('p1'), 1000)); |
1 | myPromiseRace([Promise.resolve('A'), 123, new Promise(res => setTimeout(() => res('B'), 10))]) |
1 | const p = myPromiseRace([]); |
1 | function timeout(ms) { |
Q:race 能取消其他 Promise 吗?
不能。Promise 本身不支持取消,除非你用 AbortController(fetch)或自己设计可取消任务。
Q:race 里放普通值会怎样?
会被 Promise.resolve 包装成 fulfilled Promise,通常会立刻“获胜”。
Q:race 和 any/allSettled 有什么区别?
race:谁先 settle 用谁any:谁先 fulfilled 用谁(全部 rejected 才 rejected)allSettled:等全部结束,返回每个结果状态手写 Promise.race 的核心只有一句话:
遍历 iterable,把每个元素 Promise.resolve 后接上同一个 resolve/reject,谁先 settle 就定局。
在 JavaScript 中,new运算符是面试和日常开发中都会频繁遇到的知识点。
很多人会用new,但不理解它的真正原理。
下面通过手写new,一步步拆解new运算符的底层逻辑,帮助你真正掌握 JavaScript 对象创建机制。
当我们执行下面这行代码时:
1 | const person = new Person('Alice', 25); |
JavaScript 实际上在背后做了 4 件事情:
prototypethis 绑定到新对象 理解这 4 步,是理解 new 的关键。
手写 new 的好处有三点:
接下来我们直接上代码。
1 | function myNew(constructor, ...args) { |
这段代码完整复现了 new 运算符的核心行为。
1 | const obj = Object.create(constructor.prototype); |
这一步等价于:
1 | obj.__proto__ === constructor.prototype |
也就是说:
1 | const result = constructor.apply(obj, args); |
这一步做了两件事:
this 指向新创建的对象也就是:
1 | this.name = name; |
实际上是在给 obj 添加属性。
1 | return result instanceof Object ? result : obj; |
这是很多人容易忽略的一点。
来看一个例子:
1 | function Test() { |
如果构造函数 显式返回一个对象,new 会返回这个对象,而不是 this。
所以我们在 myNew 中必须处理这种情况。
1 | function Person(name, age) { |
结果完全符合原生 new 的行为。
| 行为 | new | myNew |
|---|---|---|
| 创建新对象 | ✅ | ✅ |
| 绑定原型 | ✅ | ✅ |
| 绑定 this | ✅ | ✅ |
| 处理返回对象 | ✅ | ✅ |
可以说,myNew 已经实现了 90% 的 new 运算符行为,足以应付面试和深入理解 JS 原理。
const obj = {}?因为 {} 创建的对象原型是 Object.prototype,
而 new 创建的对象原型必须是 构造函数的 prototype。
可以,只是参数形式不同:
1 | constructor.call(obj, ...args); |
1 | p1 instanceof Person |
本质是沿着原型链查找:
1 | Person.prototype 是否在 p1 的原型链上 |
new 并不是魔法,而是一套明确的执行流程new 是理解 原型链 + this + 构造函数 的最佳方式在 Web 开发中,其实存在很多安全漏洞,其中跨站脚本攻击是最常见、危害也非常大的安全漏洞之一。
一旦网站存在 XSS 漏洞,那么攻击者就会在用户浏览器中执行恶意脚本,进而窃取 Cookie、劫持会话、篡改页面内容,甚至控制用户账号。
下面将从 原理 + 实战防御手段 的角度,系统讲解前端和全栈开发中 如何有效防御 XSS 攻击
XSS 的本质是:
攻击者将恶意 JavaScript 注入到网页中,并在其他用户的浏览器里执行。
常见的 XSS 攻击类型包括:
不论是哪种形式,核心问题都在于:
👉 用户输入被当作代码执行了
永远不要相信用户输入。
在前端可以做一些基础校验,例如:
示例(仅用于基础校验):
1 | function sanitizeInput(input) { |
⚠️ 注意:
前端校验只能作为用户体验优化,不能作为最终安全防线。
光是前端做个过滤是远远不够的,后端也需要做处理:
推荐策略:
很多 XSS 漏洞并不是输入有问题,而是输出方式不安全。
在将用户输入输出到页面时,应进行 HTML 编码:
| 原字符 | 转义后 |
|---|---|
< |
< |
> |
> |
" |
" |
' |
' |
很多模板引擎(如 Vue、React、Handlebars)默认会做转义,不要轻易关闭。
尽量避免以下写法:
1 | element.innerHTML = userInput; |
推荐安全方式:
1 | element.textContent = userInput; |
或者:
1 | const el = document.createElement('div'); |
现代前端框架本身已经内置了大量 XSS 防护机制。
v-html、dangerouslySetInnerHTML)⚠️ 一旦使用这些 API,就必须 100% 确保内容可信
错误示例:
1 | container.innerHTML = `<div>${userInput}</div>`; |
正确做法:
1 | const div = document.createElement('div'); |
CSP(Content Security Policy) 是防御 XSS 的“终极武器”之一。
示例 HTTP 头:
1 | Content-Security-Policy: |
即使攻击者成功注入脚本,只要 CSP 生效,浏览器也会拒绝执行。
因为它属于:
设置 Cookie 时加上 HttpOnly:
1 | Set-Cookie: sessionId=xxx; HttpOnly; |
这样可以:
推荐同时使用:
SecureSameSite=Strict 或 Lax提升整体安全等级。
现实中的 XSS 漏洞,大量来自第三方依赖:
建议:
npm audit最后,防御 XSS 不是“某一个技巧”,而是一整套习惯:
innerHTML对于前端开发者来说,
懂得如何防御 XSS,是走向专业的重要一步。
centos8使用yum失败,切换镜像源,更改镜像源文件,尝试清除缓存并重新建立
1 | wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo |
Centos8已不维护,只能找
puppeteer报错下载依赖 参考链接:https://www.cnblogs.com/ilizhu/p/14504049.html
报错sandbox:
1 | #添加启动参数 |
有个需求是根据一个表格的所有子表,同步到另个表格(包含子表)的数据,用google sheets里面的扩展程序里的apps 脚本功能,编写脚本。为什么要同步呢?因为原表默认是不可编辑的,打算同步过来后再进行编辑。sourceSpreadsheetId跟targetSpreadsheetId为google sheet 表格地址栏d后面的一串字符。具体的实现方式如下图方法所示:
1 | function synchronizeSheetsToAnotherFile() { |
同学们,老师刚才留意到班级上的值日表,周一到周五有好多勤劳的小蜜蜂呀。老师想问下周一值日的同学有哪些举下手?诶,老师看见了。那么同学们记住这些周一值日的同学了吗?是的,总共有六位。那如果以三个班为单位,给你一个名字,你能快速给出这名同学是星期几值日吗?你们有什么好办法吗?请你,对了,可以自己用绕口令等方式背下来。还有更好的办法吗?请你,对了,可以利用程序的方式输出出来。那该使用什么语句来表示多个条件呢?今天我们就一起来学习python中的另一种条件语句if-elif-else
现在请大家同桌两人为一小组,先来分析if-elif-else怎么实现同学名字对应星期几值日的问题,时间是8分钟。开始吧,时间到了,刚才在巡视的过程中,大家都在认真的讨论,那哪个小组代表想来分享你们的成果,你来说,你说想要判断同学是哪天值日的,只要输入他的名字,根据名字找到对应星期几输出就行了。还有补充的吗?第五小组,你们来说,他说可以弄五个数组对应星期一到星期五,把每个人的名字放进去对应的数组里,然后判断输出。你补充的非常关键。请坐。非常好,请坐,大家分析的很正确,说明大家有充分预习本节课的内容。
接下来我们就综合大家的分析,请同学们自主思考,并画出流程图,时间是5分钟,开始吧,时间到了,请这位同学来展示你的流程图,你说首先输入学生的名字,假如小花在星期一的数组里,那么输出星期一,依次类推。现在请大家尝试写出多分支结构的语句,老师提示一下,条件子句的如果需要用elif来表示。我们请这位同学将他的答案写到黑板上,请同学们来看跟你写的一样吗?哪些地方需要修改?你说将最后一个elif应该写成else,是表达都不满足条件的话,执行else后面的语句。你说要注意缩进和冒号。大家观察的很仔细,希望同学们写的时候一定要认真。请大家利用学到的if-elif语句去编写刚才你们设计的算法。我看到大家都能准确的写出来了,并且都避免了刚才的问题。
语句我们已经写出来了,那他的执行过程是什么呢?现在请同学们以同桌两人为一小组,进行讨论,一会我们请一名小组代表展示讲解,时间是7分钟,开始吧,我看到有同学在小声的讨论,时间到了,第二小组,请你们讲一讲执行过程,他结合流程图告诉我们,执行过程是依次判断输入的同学姓名符合哪个条件,如符合则输出,若都不符合则输出错误。他用图文的形式讲解,非常的直观。那现在我们就可以就着大屏幕上的流程图,总结出if-elif语句的执行过程是从if行开始依次对条件进行判断,如果正确则执行改条件下面的语句块,然后跳出if语句,如果不正确则对下一个条件进行判断,如果所有条件都不成立,则执行else下面的语句块。
新知识我们已经学完了,现在请大家编写完整程序,实现上述的执行过程,学有余力的同学,请自己思考生活中有哪些需要对多个条件进行检测的事情,自主进行编写。我们请这些同学来进行展示,他们中有的从数学出发,编写了比较三个数,求最大数问题,还有的是从生活出发,编写了出租车计费的问题。大家编写的语句和格式都很规范。
这堂课已经接近尾声了,这节课你都有哪些收获呢?大家说学会了if-elif的格式和执行过程,看来这节课的知识同学们都掌握了。其实编程离咱们的生活和学习很近,并且也没有想象中的那么难,老师希望同学们以后都可以像今天一样在课堂上积极思考,表达想法。
最后布置一个小作业,请同学们对比三种if语句特点,整理成表格。
这节课就上到这里,同学们再见。
Next版本为7.8.0,找到 /themes/next/_config.yml 文件,搜索 font 关键词,这里注意一定要将 font 里面的 enable 设置为true, 因为默认为false,然后设置 family 为自己想要的,如果family在其他平台找的,就需要修改host。(只是修改了family, 没修改enable就会不生效,所以一定一定要记得)
1 | font: |
代码可复制,还是上面同一个文件,找到以下代码段,将copy_button的enable设置为true,启用就可以啦。show_result设置true后,复制完会显示一个勾。style对应的copy_button不同的样式,看个人喜好来~
1 | codeblock: |