wangbow / blogs Goto Github PK
View Code? Open in Web Editor NEW记录点滴技术生活
License: MIT License
记录点滴技术生活
License: MIT License
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>卡片动态展示</title>
</head>
<style>
* {
margin: 0;
padding: 0;
}
ul,
li {
list-style: none;
}
ul {
width: 1260px;
margin: 0 auto;
}
ul li {
float: left;
margin-right: 20px
}
ul li:nth-child(4) {
margin-right: 0
}
.container {
position: relative;
width: 300px;
height: 400px;
overflow: hidden;
}
.img-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 300px;
overflow: hidden;
}
.img-wrapper img {
width: 100%;
height: 100%;
transition: all .5s;
}
.info {
position: absolute;
width: 100%;
height: 300px;
bottom: -200px;
background-color: rgb(247, 242, 242);
transition: all .5s;
}
.info h3 {
color: rgb(21, 74, 172);
line-height: 60px;
text-align: center;
}
.info p {
text-align: center;
font-family: 'fangsong';
font-size: 16px;
font-weight: 700;
line-height: 20px;
}
.container:hover .info {
bottom: 0px;
}
.container:hover .img-wrapper img {
transform: scale(1.2);
/* 设置图片的高斯模糊效果 */
filter: blur(1px);
}
</style>
<body>
<div>
<ul>
<li>
<div class='container'>
<div class='img-wrapper'>
<img src='./images/aa.png' />
</div>
<div class="info">
<h3>The Beautiful location</h3>
<p>this is test</p>
<p>foot is here</p>
</div>
</div>
</li>
<li>
<div class='container'>
<div class='img-wrapper'>
<img src='./images/bb.png' />
</div>
<div class="info">
<h3>The Beautiful location</h3>
<p>this is test</p>
<p>foot is here</p>
</div>
</div>
</li>
<li>
<div class='container'>
<div class='img-wrapper'>
<img src='./images/cc.png' />
</div>
<div class="info">
<h3>The Beautiful location</h3>
<p>this is test</p>
<p>foot is here</p>
</div>
</div>
</li>
<li>
<div class='container'>
<div class='img-wrapper'>
<img src='./images/dd.png' />
</div>
<div class="info">
<h3>The Beautiful location</h3>
<p>this is test</p>
<p>foot is here</p>
</div>
</div>
</li>
</ul>
</div>
</body>
</html>
当一个项目开发的越来越复杂的时候,会遇到一些问题,比如:
命名冲突:当项目由团队进行协作开发的时候,不同开发人员的变量和函数命名可能相同;即使是一个开发,当开发周期比较长的时候,也有可能会忘记之前使用了什么变量,从而导致重复命名,导致命名冲突。
文件依赖:代码重用时,引入js文件的数目可能少了,或者引入的顺序不对,比如使用boostrap的时候,需要引入jQuery,并且jQuery的文件必须要比boostrap的js文件先引入。
当使用模块化开发的时候可以避免以上的问题,并且让开发的效率变高,以及方便后期的维护
- 全局函数
function add(a, b) {
return a + b;
}
function jian(a, b) {
return a - b;
}
function mul(a, b) {
return a * b
}
function divide(a, b) {
return a / b
}
存在的问题:
1.污染了全局变量,无法保证不与其他模块发生变量名冲突。
2.模块成员之间看不出直接关系。
- 对象封装
var cal = {
add: function (a, b) {
return a + b;
},
jian: function (a, b) {
return a - b;
},
mul: function (a, b) {
return a * b
},
divide: function (a, b) {
return a / b
}
}
存在的问题:
1.暴露了所有的模块成员,内部状态可以被外部改写,不安全
2.命名空间嵌套,会越来越长
- 私有公有成员分离
var cals = (function () {
//私有的成员变量
var convert = function (data) {
return parseInt(data)
}
//提供公共方法,外部可以调用
var add = function (a, b) {
return convert(a) + convert(b);
}
var jian = function (a, b) {
return convert(a) - convert(b)
}
var mul = function (a, b) {
return convert(a) * convert(b)
}
var divide = function (a, b) {
return convert(a) / convert(b)
}
return {
add: add,
jian: jian,
mul: mul,
divide: divide
}
})()
好处:
1.利用此种方式将函数包装成一个独立的作用域,私有空间的变量和函数不会影响到全局作用域。
2.以返回值的方式得到模块的公共成员,公开公有方法,隐藏私有空间内部的属性、元素,比如注册方法中可能会记录日志。
3.可以有选择的对外暴露自身成员。
4.某种意义上来说,解决了变量命名冲突的问题。
- 模块的扩展与维护
// 计算模块
(function (calculator) {
function convert(input) {
return parseInt(input);
}
calculator.add = function(a, b) {
return convert(a) + convert(b);
}
window.calculator = calculator;
})(window.calculator || {});
// 新增需求
(function (calculator) {
calculator.mul = function (a , b) {
return a * b;
}
window.calculator = calculator;
})(window.calculator || {});
1.利用此种方式,有利于对庞大的模块的子模块划分
2.实现了开闭原则:对新增开发,对修改关闭。对于已有文件尽量不要修改,通过添加新文件的方式添加新功能。
通过Set去重
const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
const setFunction1 = arr => [...new Set(arr)];
const setFunction2 = arr => Array.from(new Set(arr));
通过includes去重复
const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
const includesArrFunction = (arr) => {
const newArr = [];
for (let i = 0; i < arr.length; i++) {
if (!newArr.includes(arr[i])) {
newArr.push(arr[i])
}
}
return newArr;
}
通过indexOf去重
const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
const idnexOfArrFunction = (arr) => {
const idnexOfArr = [];
for (let i = 0; i < arr.length; i++) {
if (idnexOfArr.indexOf(arr[i]) === -1) {
idnexOfArr.push(arr[i])
}
}
return idnexOfArr;
}
双重for循环,内层循环进行比较,注意这个splice方法会修改原数组,这里建议吧arr 深拷贝一份
const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
const xunhuanFunction = (arr) => {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
}
filter 原始数组中元素的索引等于当前索引值时返回,否则返回当前元素
const arr = [1,2,3,1,2,3, 'true', 'true', true, true, 4,4];
const filterArrFunction = (arr) => {
return arr.filter((item, index, array) => {
return array.indexOf(item) === index;
})
}
flat()方法创建一个新数组,其中所有子数组元素都以递归方式连接到该数组中,直到达到指定的深度为止 ,参数指定深度,也可以是无穷大
const arr = [1, 2, [3, 4, [5, 6]]];
const newArr = arr.flat(2);
const newArr2 = arr.flat(Infinity);
es6的扩展运算符能将二维数组变为一维,若arr中含有数组则使用一次扩展运算符,直至没有为止。
const arr = [1, 2, [3, 4, [5, 6]]];
function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
递归的遍历每一项,当为数组时则继续,不为数组则concat
const arr = [1, 2, [3, 4, [5, 6]]];
function flatten(arr) {
var result = [];
for (let i = 0, len = arr.length; i < len; i++) {
if (Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]))
} else {
result.push(arr[i])
}
}
return result;
}
reduce方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
const arr = [1, 2, [3, 4, [5, 6]]];
const reduceArr = (arr) => {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? reduceArr(item) : item);
}, [])
}
本文将解释与云计算有关的几个术语和定义
IaaS是云计算的一种模式,通过互联网将数据存储、网络和CPU能力等虚拟化资源作为一项服务提供给用户。用户可以访问他们的虚拟机,还可以在服务器上自行启动、停止和修改服务.
PaaS是云计算的一种模式,资源由供应商(第三方提供商)提供给用户。软硬件托管在PaaS基础设施上。使用这种云计算,用户按用户数量付费。
软件即服务是云计算的一种模式,资源由供应商提供给用户,可通过网络来提供,用户可以访问软件和数据库。
私有云是只有一小部分用户才能访问的云基础设施。它提供给一家组织而不是普通公众使用。私有云提供了灵活性、可扩展性、自助服务和许多可定制的功能。
与私有云相反,公有云是所有用户都可以通过互联网来访问的云基础设施。公有云还提供了灵活性、可扩展性和按使用付费。如果公有云使用正确的安全方法来正确实施,它可以像私有云一样安全。
托管云或托管服务提供商是一家IT服务提供商,允许客户部署基于云的服务。说到云基础服务,我们可能提及IaaS、PaaS、SaaS、网络以及应用程序和系统服务。这些服务通过网络提供给客户。
这种类型的云结合了公共解决方案、私有解决方案和本地解决方案。混合云是一种解决方案,主要目标是创建一个自动化、灵活、可扩展的环境,其成本比公有云和私有云要低。混合云集公有云和私有云的优点于一身。
这种类型的云由多家云服务提供商组成。多云结合使用公有云、私有云或同时提供这两者的云(名为混合云)。多云适用于用户想要某家云提供商提供特定的服务(比如公有云),又想要不同的云提供商提供数据存储服务。
主机是托管多个虚拟专用服务器机器的物理硬件。每个主机都包括处理器、内存、网络连接、硬驱和操作系统(OS)。这些部件的结合将使程序和应用程序可以为客户顺畅地运行。
通过服务器上运行的程序和应用程序执行指令的电子芯片名为**处理器单元,简称CPU。CPU的代数和功率以及核心数量对于应用程序的性能来说最重要。在云计算中,您会常常看到vCPU这个词,其意思是虚拟**处理器单元。
随机存取存储器负责临时存储服务器上运行的信息。存储在RAM中的信息可以从服务器上的任何地方非常快速地访问,CPU不必每次在需要信息时都搜索硬驱上的信息。RAM的速度比HDD或SSD快得多。
硬驱负责存储数据。服务器上的硬驱可能是SSD或NVMe。NVMe的速度比SSD快得多,比较网站加载时间方面的测试就可以发现差异。
可扩展性是云计算的一个很大的优势,可以处理不断增加的工作负载。它可以根据应用程序的需求来增加资源,对用户而言可能是月底成本效率的一个主要因素。弹性也可以使配置的资源尽可能匹配当前的需求。
负载均衡是在一台服务器无法处理所有入站流量时,将Web请求分发到多台服务器的功能。对于每分钟有数千个请求的非常庞大的应用程序而言,负载均衡系统越多越好。
复制是跨云基础设施(比如服务器或数据存储系统)共享资源的过程,以提高应用程序、数据库系统、数据或系统的可用性和可靠性。
快照是实时环境的即时副本,万一将来应用程序出岔子,可用于恢复。此外,向生产环境发布新内容之前,拍摄快照以用于测试开发目的。有了快照,您可以轻松地将数据立即恢复到环境中。
数据库是在单独的表中组织的数据集合,以便更好地查询和访问。
在不同的存储系统或服务器之间移动数据的过程名为数据库迁移。
内容分发网络安全保护提供了一系列广泛的机制,以防范对网站实施的DDoS、黑客攻击或垃圾邮件攻击。此外,CDN可以缓解网络连接速度慢的问题。
云市场是云托管提供商开展运营时配套的在线市场。客户可以根据自己的需要,订阅和订购应用程序。
ES6模块主要有2个功能:export 和 import
export用于对外导出变量,函数,对象等模块的接口
import用于在一个模块中引入export导出的变量,函数,对象等
export 和 import 单个变量
a.js
export const name = "benben"
b.js
import {name} from "./a";
console.log(name); //benben
export 和 import 多个变量
a.js
export const name1 = "benben1";
export const name2 = "benben2";
b.js
import {name1,name2} from "./a";
console.log(name1,":", name2); //benben1:benben2
函数也是同理;
1.export和export default均可用于导出常量,函数,文件,模块等
2.可以通过import +(常量,函数,文件,模块)名的方式,将其导入,以便对其使用
3.在一个文件或模块中,export,import可以有多个, export default仅有一个
4.通过export方式导出,在导入时要加{ },export default则不需要
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
var lengthOfLongestSubstring = function(s) {
let arr = [], max=0;
for(let i=0;i<s.length;i++){
const index = arr.indexOf(s[i]);
if(index!=-1){
arr.splice(0,index+1);
}
arr.push(s[i]);
max = Math.max(arr.length,max);
}
return max;
};
var s = 'pwwkew';
lengthOfLongestSubstring(s);
为一个对象提供一个代用品或占位符,以便控制对它的访问
当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。
替身对象对请求做出一些处理之后, 再把请求转交给本体对象
代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一 些额外的事情
代理模式主要有三种:保护代理、虚拟代理、缓存代理
保护代理主要实现了访问主体的限制行为,以过滤字符作为简单的例子
function message(message) {
console.log(message)
}
function proxyMessage(info) {
if (!info) {
return;
}
info = info.replace(/操/g, '')
message(info);
}
proxyMessage("操,贱人");
它的意图很明显,在访问主体之前进行控制,没有消息的时候直接在代理中返回了,拒绝访问主体,这数据保护代理的形式
虚拟代理在控制对主体的访问时,加入了一些额外的操作
在滚动事件触发的时候,也许不需要频繁触发,我们可以引入函数节流,这是一种虚拟代理的实现
function debounce(fn, delay) {
delay = delay || 20;
var time = null;
return function () {
var arg = arguments;
clearTimeout(time);
// time = null;
time = setTimeout(function () {
fn.apply(this, arg);
}, delay);
}
}
var count = 0;
// 主体
function scrollHandle(e) {
console.log(e.type, ++count); // scroll
}
// 代理
var proxyScrollHandle = (function () {
return debounce(scrollHandle, 500);
})();
window.onscroll = proxyScrollHandle;
单例模式(Singleton Pattern)是 设计模式中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
function Person(name) {
this.name = name;
}
const singleton = (function () {
let instance = null;
return {
getInstance: function () {
if (!instance) {
instance = new Person("aaa");
}
return instance;
}
}
})();
let s1 = singleton.getInstance();
let s2 = singleton.getInstance();
console.log(s1 == s2); //true
console.log(s1.name); //aaa
主要通过http请求的response hearder 中的cache-control (http1.1)和 expire(http1.0) 俩个字段来控制
cache-control:值可以为"public", "private", "max-age=xxx", 一般用xxx秒内再次访问这个资源,均使用本地缓存来处理,不在向服务器发起请求
问题点:当发布新版本的时候,后台接口也同步更新了,那就gg了。有缓存的用户还在使用旧接口,而那个接口已经被后台干掉了。怎么办?
协商缓存最大的问题是:每次请求都要去服务器进行缓存有效性的验证
缓存的意义在于,更多的使用本地资源,提高用户体验,降低服务器的压力。**
所以: 最佳实践就是尽可能的命中强缓存,同时在更新版本的时候,让客户端的缓存失效
在更新版本之后,如何让用户第一时间使用最新的资源文件呢?机智的前端们想出了一个方法,在更新版本的时候,顺便把静态资源的路径改了,这样,就相当于第一次访问这些资源,就不会存在缓存的问题
webpack打包文件的时候,使用了chunkhash
综上所述,我们可以得出一个较为合理的缓存方案:
HTML:使用协商缓存。
CSS&JS&图片:使用强缓存,文件命名带上hash值。
在理解拷贝之前我们要先熟悉变量存储类型 变量的数据类型分为 基本数据类型 (值类型)和 复杂数据类型(引用数据类型)
基本数据类型的值是直接存放在栈内存的 而复杂数据类型的栈内存保存的是内存地址 值都是保存在堆内存中
概念
我们可以理解为 浅拷贝就是创建一个新对象 这个对象有着被拷贝对象的对象属性值
如果是基本数据类型 拷贝的就是这个基本数据类型的值 改变新对象不会导致原来的数据发生改变
如果属性是复杂数据类型 拷贝的就是复杂数据类型的内存地址
所以如果其中一个对象改变了这个地址 就会影响到另外一个对象 就会导致原数据发生改变
这里借用GitHub中ConardLi大佬的图片来方便我们理解
语法 Object.assign(target, … sources)
第一个参数是目标对象
第二分参数是原对象(也就是被拷贝的对象)
这是es6提供给我们的新方法 我们使用的时候需要注意兼容性的问题
还有个需要注意的是 这个方法一般用来浅拷贝对象 用来处理数组的时候 会把数组视为对象
const obj = {
name:'xiaoming',
age:18,
hobby:['football','music']
}
const obj2 = Object.assign({}, obj)
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);
方法2 利用展开运算符进行浅拷贝
const obj = {
name:'xiaoming',
age:18,
hobby:['football','music']
}
const obj2 = {...obj};
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);
const obj = {
name:'xiaoming',
age:18,
hobby:['football','music']
}
const obj2 = {};
for(const key in obj){
obj2[key] = obj[key]
}
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);
深拷贝指的是 将被拷贝的对象从内存中完全拷贝一份出来 在堆内存中开辟一个新的区域来存放新对象 并且修改新对象不会影响原来对象
我们在借用一次 GitHub中ConardLi大佬的图片来方便我们理解
const obj = {
name:'xiaoming',
age:18,
hobby:['football','music']
}
function deepClone(data){
if(!data ){
return data;
}
const target = Array.isArray(data)? [] :{};
for(const key in data){
const item = data[key];
if(typeof item === 'function'){
target[key] = new Function(`return ${item.toString()}`)();
}else if(typeof item === 'object'){
target[key] = deepClone(item);
}else{
target[key] = item
}
}
return target;
}
const obj2 = deepClone(obj);
obj2.hobby.push('swim');
console.log('obj',obj);
console.log('obj2',obj2);
乍看 JSON.parse(JSON.stringify()) 不太明白 我们来解释一下
JSON.parse() 是一个生成新对象的方法
JSON.stringify() 是把原对象序列化为一个JSON字符串
简单的说 就是先把源对象序列化为一个JSON字符串 然后利用JSON.parse()方法转变为一个新对象
这也是我们最常用的方法
我们来使用看看结果如何:
const obj = {
name:'xiaoming',
age:18,
hobby:['football','music']
};
const objString = JSON.stringify(obj);
const obj2 = JSON.parse(objString);
obj2.hobby.push('swim');
console.log(obj);
console.log(obj2);
有以下几种情况时,不能正确的进行深拷贝:
1.obj里面有new Date(),深拷贝后,时间会变成字符串的形式。而不是时间对象;
let a = {
name: 'a',
date: [new Date(1536627600000), new Date(1540047600000)],
};
let b = JSON.parse(JSON.stringify(a))
console.log(a,b);
2.obj里有RegExp、Error对象,则序列化的结果会变成空对象{};
const a = {
name: 'a',
date: new RegExp('\\w+'),
};
const b = JSON.parse(JSON.stringify(a));
a.name = 'test'
console.log( a, b)
3.obj里有function,undefined,则序列化的结果会把function或 undefined丢失
const a = {
name: 'a',
date: function hehe() {
console.log('fff')
},
};
const b = JSON.parse(JSON.stringify(a));
a.name = 'test'
console.log(a, b)
4.obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null;
const a = {
name: 'a',
date: NaN,
};
const b = JSON.parse(JSON.stringify(a));
a.name = 'test'
console.log(a, b)
HTML CODE
<input type = "text" id = "root" />
**JS CODE **
function throttle(fn) {
let flag = true
return function () {
if (!flag) {
return false
}
flag = false
setTimeout(() => {
fn()
flag = true;
},1000)
}
}
function sayOk(){
console.log("throttle success");
}
const ele = document.getElementById("root")
ele.addEventListener("input" , throttle(sayOk))
** 编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。**
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/group-anagrams-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
var strs = ["eat", "tea", "tan", "ate", "nat", "bat"];
let groupAnagrams = function (strs) {
let a, j, t = 0, result = [], map = new Map();
for (let b of strs) {
// 吧数组的每个字符串转换成数组,并进行排序,在转回为字符串
a = b.split("").sort().join("");
// 获取每个字符串数组对应的索引
map.has(a) ? (j = map.get(a)): map.set(a, j = t++);
// 放入result 数组的不同位置
(result[j] = result[j] ||[]).push(b);
}
return result;
};
1.改变当前函数的this指向
2.还会让当前函数执行
Function.prototype.mycall = function (context) {
//通过Object对基础数据类型,进行包装成对象,如果是null || undefined, context就是全局的window对象
context = context ? Object(context) : window;
// 吧当前的函数挂载到context对象的fn属性上
context.fn = this;
const args = [];
//通过args来对参数进行存储
for(let i =1; i<arguments.length;i++){
args.push(`arguments[${i}]`);
}
//通过eval执行字符串
const result = eval(`context.fn(${args})`);
//执行完毕,删除context.fn
delete context.fn;
return result;
}
function fn1() {
console.log(this, arguments);
}
fn1.mycall({ name: "aaa" }, '1', '2');
fn1.mycall('hello','ww','wqq');
function fn2(){
console.log(this,2);
}
多个call执行,会让call方法中的this改变成fn2这个参数
fn1.mycall.mycall(fn2) ;
多重判断时使用 Array.includes
让我们看一下下面这个例子:
`
function test(fruit) {
if (fruit == 'apple' || fruit == 'strawberry') {
console.log('red');
}
}
`
第一眼,上面这个例子看起来没问题。如果我们有更多名字叫 cherry 和 cranberries 的红色水果呢?我们准备用更多的 || 来拓展条件语句吗?
我们可以用 Array.includes (Array.includes)重写条件语句。
`
function test(fruit) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
if (redFruits.includes(fruit)) {
console.log('red');
}
}
`
我们把红色的水果(red fruits)这一判断条件提取到一个数组。这样一来,代码看起来更整洁。
**** 更少的嵌套,尽早 Return
让我们拓展上一个例子让它包含两个条件。
`
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// 条件 1: fruit 必须有值
if (fruit) {
// 条件 2: 必须是red的
if (redFruits.includes(fruit)) {
console.log('red');
// 条件 3: quantity大于10
if (quantity > 10) {
console.log('big quantity');
}
}
} else {
throw new Error('No fruit!');
}
}
// 测试结果
test(null); // error: No fruits
test('apple'); // print: red
test('apple', 20); // print: red, big quantity
`
在上面的代码, 我们有:
我个人遵循的规则一般是在发现无效条件时,尽早Return。
`
/_ 当发现无效语句时,尽早Return _/
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// 条件 1: 尽早抛出错误
if (!fruit) throw new Error('No fruit!');
// 条件 2: 必须是红色的
if (redFruits.includes(fruit)) {
console.log('red');
// 条件 3: 必须是大质量的
if (quantity > 10) {
console.log('big quantity');
}
}
}
`
这样一来,我们少了一层嵌套语句。这种编码风格非常好,尤其是当你有很长的if语句的时候(想象你需要滚动到最底层才知道还有else语句,这并不酷)
我们可以通过 倒置判断条件 & 尽早return 进一步减少if嵌套。看下面我们是怎么处理判断 条件2 的:
`
/_ 当发现无效语句时,尽早Return _/
function test(fruit, quantity) {
const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];
// 条件 1: 尽早抛出错误
if (!fruit) throw new Error('No fruit!');
// 条件 2: 当水果不是红色时停止继续执行
if (!redFruits.includes(fruit)) return;
console.log('red');
// 条件 3: 必须是大质量的
if (quantity > 10) {
console.log('big quantity');
}
}
`
通过倒置判断条件2,我们的代码避免了嵌套语句。这个技巧在我们需要进行很长的逻辑判断时是非常有用的,特别是我们希望能够在条件不满足时能够停止下来进行处理。
而且这么做并不困难。问问自己,这个版本(没有嵌套)是不是比之前的(两层条件嵌套)更好,可读性更高?
但对于我,我会保留先前的版本(包含两层嵌套)。这是因为:
因此,应当尽力减少嵌套和尽早return,但不要过度.
3.使用默认参数和解构
我猜下面的代码你可能会熟悉,在JavaScript中我们总是需要检查 null / undefined的值和指定默认值:
`
function test(fruit, quantity) {
if (!fruit) return;
// 如果 quantity 参数没有传入,设置默认值为 1
const q = quantity || 1;
console.log(We have ${q} ${fruit}!
);
}
//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
实际上,我们可以通过声明 默认函数参数 来消除变量 q。
function test(fruit, quantity = 1) {
// 如果 quantity 参数没有传入,设置默认值为 1
if (!fruit) return;
console.log(We have ${quantity} ${fruit}!
);
}
//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!
`
这更加直观,不是吗?注意,每个声明都有自己的默认参数.
例如,我们也能给fruit分配默认值:function test(fruit = 'unknown', quantity = 1)。
如果fruit是一个object会怎么样?我们能分配一个默认参数吗?
`
function test(fruit) {
// 当值存在时打印 fruit 的值
if (fruit && fruit.name) {
console.log (fruit.name);
} else {
console.log('unknown');
}
}
//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
`
看上面这个例子,我们想打印 fruit 对象中可能存在的 name 属性。否则我们将打印unknown。我们可以通过默认参数以及解构从而避免判断条件 fruit && fruit.name
`
// 解构 - 仅仅获取 name 属性
// 为其赋默认值为空对象
function test({name} = {}) {
console.log (name || 'unknown');
}
// test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
`
由于我们只需要 name 属性,我们可以用 {name} 解构出参数,然后我们就能使用变量 name 代替 fruit.name。
我们也需要声明空对象 {} 作为默认值。如果我们不这么做,当执行 test(undefined) 时,你将得到一个无法对 undefined 或 null 解构的的错误。因为在 undefined 中没有 name 属性。
如果你不介意使用第三方库,这有一些方式减少null的检查:
`
function test(fruit) {
// 获取属性名,如果属性名不可用,赋默认值为 unknown
console.log(__.get(fruit, 'name', 'unknown');
}
// test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple
`
你可以在jsbin运行demo代码。除此之外,如果你是函数式编程的粉丝,你可能选择使用 Lodash fp,Lodash的函数式版本(方法变更为get或者getOr)
倾向于对象遍历而不是Switch语句
让我们看下面这个例子,我们想根据 color 打印出水果:
`
function test(color) {
// 使用条件语句来寻找对应颜色的水果
switch (color) {
case 'red':
return ['apple', 'strawberry'];
case 'yellow':
return ['banana', 'pineapple'];
case 'purple':
return ['grape', 'plum'];
default:
return [];
}
}
// test results
test(null); // []
test('yellow'); // ['banana', 'pineapple']
`
上面的代码看起来没有错误,但是我找到了一些累赘。用对象遍历实现相同的结果,语法看起来更简洁:
`
const fruitColor = {
red: ['apple', 'strawberry'],
yellow: ['banana', 'pineapple'],
purple: ['grape', 'plum']
};
function test(color) {
return fruitColor[color] || [];
}
`
或者你也可以使用 Map实现相同的结果:
`
const fruitColor = new Map()
.set('red', ['apple', 'strawberry'])
.set('yellow', ['banana', 'pineapple'])
.set('purple', ['grape', 'plum']);
function test(color) {
return fruitColor.get(color) || [];
}
`
Map是一种在 ES2015 规范之后实现的对象类型,允许你存储 key 和 value 的值。
但我们是否应当禁止switch语句的使用呢?答案是不要限制你自己。从个人来说,我会尽可能的使用对象遍历,但我并不严格遵守它,而是使用对当前的场景更有意义的方式。
** 对所有/部分 判断使用Array.every & Array.some
这最后一个建议更多是关于利用 JavaScript Array 的内置方法来减少代码行数。看下面的代码,我们想要检查是否所有水果都是红色:
`
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
let isAllRed = true;
// 条件:所有水果都是红色
for (let f of fruits) {
if (!isAllRed) break;
isAllRed = (f.color == 'red');
}
console.log(isAllRed); // false
}
`
代码那么长!我们可以通过 Array.every减少代码行数:
`
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
const isAllRed = fruits.every(f => f.color == 'red');
console.log(isAllRed); // false
}
`
现在更简洁了,不是吗?相同的方式,如果我们想测试是否存在红色的水果,我们可以使用 Array.some 一行代码实现。
`
const fruits = [
{ name: 'apple', color: 'red' },
{ name: 'banana', color: 'yellow' },
{ name: 'grape', color: 'purple' }
];
function test() {
// 条件:任何一个水果是红色
const isAnyRed = fruits.some(f => f.color == 'red');
console.log(isAnyRed); // true
}
`
``
const obj = {
name: "benben",
age: 22,
hobby: {
name: "play"
}
}
const getType = (obj) => {
return Object.prototype.toString.call(obj).slice(8, -1);
}
const deepCopy = (obj) => {
let newObj;
const type = getType(obj);
if (type === "Object") {
newObj = {};
} else if (type === "Array") {
newObj = [];
} else {
return obj;
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (getType(obj[key]) !== "Object" && getType(obj[key]) !== "Array") {
newObj[key] = obj[key]
} else {
newObj[key] = deepCopy(obj[key]);
}
}
}
return newObj;
}
const obj2 = deepCopy(obj);
obj2.hobby.name = "chifan";
console.log("obj2", obj2.hobby.name); //obj2 chifan
console.log("obj", obj.hobby.name); //obj play
本次主要介绍基于chrome浏览器对node进行调试。
基于chrome浏览器调试需要安装node-inspector
npm install -g node-inspector
node-inspector
调试监听默认占用8080端口,修改端口可以通过
node-inspector --web-port 9999
node --debug app.js
上面的方式不会在程序第一行停留,要在程序第一行停留可通过以下方式启动
node --debug-brk=3333 gulp.js
在chrome浏览器中打开以下链接:
http://127.0.0.1:8080/?port=3333
const errorMessage = {
isRequired: '不能为空 ',
minLength: '长度必须>5',
isNumber: '必须是数字'
};
const rulesMap = {
isRequired: function (value, errorMsg) {
if (value === '') {
return errorMsg || errorMessage['isRequired'];
}
},
minLength: function (value, length, errorMsg) {
if (value.length < length) {
return errorMsg || errorMessage['minLength'];
}
},
isNumber: function (value, errorMsg) {
if (!/\d+/.test(value)) {
return errorMsg || errorMessage['isNumber'];
}
}
};
function Validator() {
this.items = [];
}
Validator.prototype = {
constructor: Validator,
add: function (value, rule, errorMsg) {
const arg = [value];
if (rule.indexOf('minLength') != -1) {
const str = rule.split(':');
rule = str[0];
arg.push(str[1]);
};
arg.push(errorMsg);
this.items.push(function () {
return rulesMap[rule].apply(this, arg);
})
},
start: function () {
for (let i = 0; i < this.items.length; i++) {
const rest = this.items[i]();
if (rest) {
console.log(rest)
}
}
}
};
const validator = new Validator();
validator.add('', 'isRequired', '必须有值');
validator.add('2131321', 'minLength:10', '必须长度大于10');
validator.add('adada', 'isNumber', '必须是数字');
validator.start()
html code:
<input id="root" type="text" />
js code:
function debounce(fn) {
let time = null;
return function () {
if (time) {clearTimeout(time); }
time = setTimeout(() => {
fn.call(this); //>= 吧input的this绑定到当前函数fn上
}, 500);
}
}
function sayOk() {
console.log("fangdou success");
}
const ele = document.getElementById("root");
ele.addEventListener("input", debounce(sayOk))
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
const [p1, p2, p3, p4, p5] = document.querySelectorAll('p');
const m1 = new Map();
const m2 = new Map();
const m3 = new Map();
const m4 = new Map();
const m5 = new Map();
m1.set('color', 'red').set('backgroundColor', 'yellow').set('fontSize', '40px');
m2.set('color', 'green').set('backgroundColor', 'pink').set('fontSize', '40px');
m3.set('color', 'blue').set('backgroundColor', 'orange').set('fontSize', '40px');
m4.set('color', '#ffaabb').set('backgroundColor', '#ccc').set('fontSize', '40px');
m5.set('color', '#ffqqqq').set('backgroundColor', 'red').set('fontSize', '40px');
const m = new Map()
m.set(p1, m1).set(p2, m2).set(p3, m3).set(p4,m4).set(p5,m5);
console.log('m', m)
m.forEach((value,ele)=>{
value.forEach((item,key)=>{
ele.style[key] = item;
})
})
递归实现一个特别简单的深拷贝
const obj = {
a: 1,
b: 2,
newObj: {
name: "hello"
}
}
const deepClone = function (resource) {
let result = {};
for (let key in resource) {
result[key] = typeof resource[key] == "object" ? deepClone(resource[key]) : resource[key]
}
return result;
}
const obj2 = deepClone(obj);
obj.newObj.name = "hi";
console.log("obj", obj); // { a : 1, b : 2, newObj: { name:"hi" }}
console.log("obj2", obj2);// { a : 1, b : 2, newObj: { name:"hello" }}
在CSS的官方文档的说明中,BFC:全称 Block formatting contexts,直译过来就是块级格式化上下文。
讲到这里你可能还是一头雾水,我们这里先停下来讲几个基本的概念以便有助于你接下来的理解。
它是指页面中一块独立的渲染区域,这块区域有自己独立的渲染规则,规则取决于这块区域的Box属性。
这套独立的规则规定渲染区域内的子元素如何定位以及和其他元素之间的相互关系
BFC布局规则
内部的Box会在垂直方向,一个接一个地放置。
Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
BFC的区域不会与float box重叠。
BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
计算BFC的高度时,浮动元素也参与计算
根元素或其它包含它的元素
浮动 (元素的 float 不是 none)
绝对定位的元素 (元素具有 position 为 absolute 或 fixed)
非块级元素具有 display: inline-block,table-cell, table-caption, flex, inline-flex
块级元素具有overflow ,且值不是 visible
弹性布局 flex
根据上面的描述,BFC区域有一个特点,那就是独立。不会影响外部元素。根绝这个特性就能解决很多布局问题。
1,解决外边距的塌陷问题(垂直塌陷)
<div class='div> </div>
<div class='div> </div>
.div{
width:50px;
height:50px;
background: red;
margin: 50px;
}
<div class='parent'>
<div class='div1> </div>
</div>
<div class='div2'> </div>
.parent{
overflow:hidden;
}
.div{
width:50px;
height:50px;
background: red;
margin: 50px;
}
2,利用BFC解决包含塌陷
有时候我们给子元素加margin可能会带着父元素一起跑
很显然,我们只是想要子元素距离父元素50px,而不是整个父元素都一起跑。这个时候用padding可以解决问题,但是用BFC同样可以解决
<div class="parents">
<div class="child"></div>
</div>
.parents{
width: 300px;
border: 5px solid #EF6C72;
}
.child{
width:100px;
height: 100px;
background-color: black;
float: left;
}
可以看到,父元素里面包含了一个子元素,在子元素浮动的情况下,脱离文档流,父元素高度不会被子元素撑开,这是如果我们需要让父元素被浮动的子元素撑开,根据BFC布局规则 计算BFC的高度时,浮动元素也参与计算,我们让父级元素生成一个BFC,则父元素内的子元素也会计算高度
可以通过以下方式生成一个BFC:
.parents:{
/*overflow: hidden;*/
/*float: left;*/
/*display: inline-block;*/
/*position: absolute;*/
}
准备阶段:
const docText =
`
### 集成概述
通过 Github 事件部署 devops 流水线.
可以用过捕获 Github 事件,对计算资源或者其他下游系统进行触发部署,完成自动流水线作业。
### 集成用例
通过 Github 事件完成事件通知.
EventBridge 可快速捕获和接收 Github 变更消息,并将该变更项目对下游系统如钉钉,短信等进行通知与告警,帮助运维同学快速感知代码变更,并进行相关预案。
### 集成架构
![Alt text](https://img.alicdn.com/imgextra/i1/O1CN01oTL2S529JZsRVTcHQ_!!6000000008047-2-tps-2296-1448.png)
`;
<ReactMarkdown
components={{
code({ node, inline, className, children, ...props }) {
return (
<SyntaxHighlighter
showLineNumbers={true} //是否展示左侧行数
style={vscDarkPlus} //主题风格
language={'JavaScript'} //语言类型选择, 如:css, jsx,javascript等
PreTag='div'
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
)
}
}}
>
{docText.replace(/\n$/, '')}
</ReactMarkdown>
<ReactMarkdown children={docText} />
请看一段代码
[] == false //true;
!![] == true //true;
!!'' == false //true;
[1] == "1" //true;
"" == 0 //true;
"" == false //true;
[1] == true //true;
null == false //false
null == "" //false
为什么[] == false ? 是true
1.因为2个类型不同,先要进行类型转换,然后才能进行比较;
2.js语法规定,如果2个类型在进行 == 比较的时候,如果有boolean类型的值存在,先要把boolean类型的值转换为数字, false 转换为 0 ,true 转换为1
3.引用类型的对象通过toString(),转换为基本类型,然后进行比较; [].toString() // ""
4."" 转换为数字为 0
5.0 == 0 // true
为什么 [] == 0 ? 是true
1.先进行类型转换, [].toString() // ""
2."" 转换为 Number("") //0
3.0 == 0 //true
我们来尝试一些更奇葩的对象和字符串的 == 比较
'[object Object]'=={}// true
1.先进行类型转换,{}.toString() //'[object Object]'
2.'[object Object]' == '[object Object]' //true
为什么“” == 0
1.字符串和数字进行比较,会吧字符串转换为数字,然后进行比较
2."" 转换为 0
3.0 == 0 //true
为什么 null == 0 是 false呢?
1.由于null没有toString(),不能进行转换
为什么 null == undefiend 是 true?
js 底层语法实现
为什么 !![] 是 true?
1.这里面不涉及任何 == 比较, 和上面的题目完全是两类题目, 千万不可搞混
2.此题直接判断!!后面的变量 是不是不是 Falsy(假值) 即可, 只要不是Falsy这几个值, 其余情况都是 true
3.Falsy假值包括:"",0.null.undefined,false,NaN
** 实现一个算法,确定一个字符串 s 的所有字符是否全都不同。 **
输入: s = "leetcode"
输出: false
输入: s = "abc"
输出: true
map key的唯一性
var isUnique = function(astr) {
const arr = astr.split(''); //吧一个完整的字符串按照""进行拆分成数组,每个字符对应数组中的一个元素
const map = new Map();
for(let i =0; i<arr.length;i++){
map.set(arr[i],i);
}
const newArr = Array.from(map);
if(newArr.length === arr.length){
return true
}
return false;
};
Set 去重解决
var isUnique = function(astr){
return new Set(astr).size === astr.length
}
保证一个类只有1个实例,并提供一个可以访问它的全局访问点
一个类只有1个实例, 提供全局访问点
假设要设置一个销售员,多次调用也仅设置一次,我们可以使用闭包缓存一个内部变量来实现这个单例
function SetManager(name){
this.name = name;
}
SetManager.prototype.getName = function(){
console.log('name',this.name);
}
function singleTonSetManager(fn){
let instance = null;
return function(){
if(!instance){
instance = fn.apply(this, arguments);
}
return instance;
}
}
const getSetManagetSinleton = singleTonSetManager(function(name){
const manager = new SetManager(name);
return manager;
});
getSetManagetSinleton('abc').getName();
getSetManagetSinleton('abc').getName();
function Ruler(name){
this.name = name;
}
Ruler.prototype.getName = function(){
console.log('ruler is ', this.name)
}
const getSetRulesSingleton = singleTonSetManager(function(name){
const ruler = new Ruler(name);
return ruler;
});
getSetRulesSingleton('benben').getName();
getSetRulesSingleton('benben').getName();
function isPrime(n) {
// 为2是因为质数为2,而且质数不能为1和自身不能被其他自然数整除的数叫做质数;
for (let i = 2; i < n; i++) {
if (n % i === 0) {
console.log(n, i);
return false //质数
}
}
return true //非质数
}
console.log(isPrime(5)); //true
console.log(isPrime(9)); //false
Array.prototype.map() 理解
arr.map(function callback(currentValue[, index[, array]]) //> 返回一个新数组对象
这个callback一共可以接收三个参数,
第一个参数代表当前被处理的元素,
第二个参数代表该元素的索引。
第三个参数代表原数组对象
而parseInt则是用来解析字符串的,使字符串成为指定基数的整数。
parseInt(string, radix)
接收两个参数
第一个表示被处理的值(字符串)
第二个表示为解析时的基数 (radix | 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。)
了解这两个函数后,我们可以模拟一下运行情况
parseInt('1', 0) //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回1
parseInt('2', 1) //基数为1(1进制)表示的数中,1不在radix的范围内,所以无法解析,返回NaN
parseInt('3', 2) //基数为2(2进制)表示的数中,radix是2的时候, 只有2中数值(0,1),都不在3的范围内,所以无法解析,返回NaN
map函数返回的是一个数组,所以最后结果为[1, NaN, NaN]
Demo test:
['11', '11', '11',11].map(parseInt) ?
解析:
parseInt('11', 0), //radix为0时,且string参数不以“0x”和“0”开头时,按照10为基数处理。这个时候返回11
parseInt('11', 1), //基数为1(1进制)表示的数中,1不在radix的范围内,所以无法解析,返回NaN
parseInt('11', 2), //radix为2时, (2的0次方 是1 )* 1 + (2的1次方 是2) *1这个时候返回3
parseInt('11', 3), //radix为3时,(3的0次方 是1 )* 1 + (3的1次方 是3) *1这个时候返回4
map函数返回的是一个数组,所以最后结果为[11, NaN, 3, 4]
最后附上MDN上对于这两个函数的链接,具体参数大家可以到里面看
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/parseInt
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Function.prototype.myapply = function(context,args){
context = context ? Object(context) : window;
context.fn = this;
if(!args){
return context.fn();
}
const result = eval(`context.fn(${args})`);
delete context.fn;
return result;
}
const urlhref = 'http://baidu/src/index.html';
const getParam = function (urlhref) {
let urlParams = urlhref.split('?');
//如果没有?后面的参数,返回‘’
if (urlParams[0] === urlhref) {
return ''
}
let paramsArr = urlParams[1].split('&');
const obj = {};
for (let i = 0; i < paramsArr.length; i++) {
let param = paramsArr[i].split('=');
obj[param[0]] = param[1];
}
return obj;
}
为一个对象提供一个代用品或占位符,以便控制对它的访问
当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。
替身对象对请求做出一些处理之后, 再把请求转交给本体对象
代理和本体的接口具有一致性,本体定义了关键功能,而代理是提供或拒绝对它的访问,或者在访问本体之前做一 些额外的事情
代理模式主要有三种:保护代理、虚拟代理、缓存代理
保护代理主要实现了访问主体的限制行为,以过滤字符作为简单的例子
function sendMessage(msg) {
console.log(msg);
}
function proxySendMsg(msg) {
if (typeof msg === 'undefined') {
console.log('deny');
return;
}
msg = ('' + msg).replace(/泥\s*煤/g, '');
sendMessage(msg);
}
proxySendMsg('泥煤呀泥 煤')
proxySendMsg();
虚拟代理在控制对主体的访问时,加入了一些额外的操作
在滚动事件触发的时候,也许不需要频繁触发,我们可以引入函数防抖,这是一种虚拟代理的实现
function debounce(fn, delay) {
delay = delay || 200;
var time = null;
return function () {
const arg = arguments;
clearTimeout(time);
time = setTimeout(() => {
fn.apply(this, arg);
}, delay);
}
}
let count = 0;
function scollHandler(e) {
console.log(e.type, ++count);
}
const proxyScrollHandler = (function () {
return debounce(scollHandler, 200);
})()
window.onscroll = proxyScrollHandler;
缓存代理可以为一些开销大的运算结果提供暂时的缓存,提升效率
function add() {
const arg = [].slice.call(arguments);
return arg.reduce(function (a, b) {
return a + b;
})
}
const proxyAdd = (function () {
const cache = [];
return function () {
const arg = [].slice.call(arguments).join(',');
if (cache[arg]) {
return cache[arg];
} else {
cache[arg] = arg;
const ret = add.apply(this, arguments);
return ret;
}
}
})();
console.log(
add(1, 2, 3, 4),
add(1, 2, 3, 4),
proxyAdd(10, 20, 30, 40),
proxyAdd(10, 20, 30, 40)
);
function Person(name) {
this.name = name;
}
Person.prototype.getName = function () {
return this.name;
}
const singletonPerson = function (fn) {
let instance = null;
return function () {
if (!instance) {
instance = fn.apply(this, arguments)
}
return instance;
}
};
const getSingeTon = singletonPerson(function (name) {
let p1 = new Person(name);
return p1;
})
const s1 = getSingeTon("lol");
const s2 = getSingeTon("cf");
console.log("s1 == s2" , s1 == s2 ) //true
直接上题:
function num(n) {
while (n-- > 0) {
console.log(n);
}
}
num(3)
为什么会输出2,1,0 ,而不是2,1呢?
解析:
后置递减(--): 在变量的前面,先赋值后在执行减法操作(n--);
function num(n) {
while (n-- > 0) {
console.log(n);
}
}
num(3)
输出:2,1
解析:
前置递减(--): 在变量的前面,先自身执行减法操作后在赋值(--n);
function num(n,m){
console.log(n);
if(m>n){
num(n+1,m);
console.log(n);
}
}
num(2,5);
输出:2,3,4,5,4,3,2
解析:
function checkedType(data) {
return Object.prototype.toString.call(data).slice(8, -1);
}
function deepClone(target) {
let result = null;
if (checkedType(target) === 'Object') {
result = {};
} else if (checkedType(target) === 'Array') {
result = [];
} else {
return target;
}
for (const key in target) {
const value = target[key];
if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
result[key] = deepClone(target[key])
} else {
result[key] = target[key]
}
}
return result;
};
测试1
const obj = ['123', '456']
const obj2 = deepClone(obj);
console.log('obj1', obj);
console.log('obj2', obj2);
obj2[0] = 'wolalda';
console.log('obj1', obj);
console.log('obj2', obj2);
测试2
const obj = {
name:'name1',
action:{
play1:'games1',
play2:'games2'
}
};
const obj2 = deepClone(obj);
console.log('obj1', obj);
console.log('obj2', obj2);
obj2.name='qqd';
console.log('obj1', obj);
console.log('obj2', obj2);
setTimeout(function() {
console.log('setTimeout');
},1000)
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then');
})
console.log('console');
1.setTimeout是个宏任务,放入宏任务队列中
2.new Promise 同步代码,立即执行console.log('promise'),然后看到微任务then,因此将其放入微任务的任务队列中
3.执行同步代码console.log('console')
4.主线程的宏任务,已经执行完毕,接下来要执行微任务,因此会执行Promise.then,到此,第一轮事件循环执行完毕
5.第二轮事件循环开始,先执行宏任务,即setTimeout的回调函数,然后查找是否有微任务,没有,时间循环结束
到此做个总结,事件循环,先执行宏任务,其中同步任务立即执行,异步任务,加载到对应的的Event Queue中(setTimeout等加入宏任务的Event Queue,Promise.then加入微任务的Event Queue),所有同步宏任务执行完毕后,如果发现微任务的Event Queue中有未执行的任务,会先执行其中的任务,这样算是完成了一次事件循环。接下来查看宏任务的Event Queue中是否有未执行的任务,有的话,就开始第二轮事件循环,依此类推。
// 有一个嵌套很深层级的对象,key的形式都是a_b
const a = {
a_y: {
a_z: {
y_x: 6
},
b_c: 1
}
};
解决方案一
1.吧对象转换成JSON字符串,然后通过正则替换掉_,
2.吧JSON字符串在解析成对象,然后返回
function reglarExpress(obj){
const str = JSON.stringify(obj).replace(/_/g,"");
try{
const result = JSON.parse(str);
return result;
}catch(error){
console.log('error', error)
}
}
const result = reglarExpress(a);
console.log('result', result);
解决方案二
递归解决,每次递归调用之前需要,判断一下传入的参数,是不是对象
function reglarExpress(obj) {
const keys = Object.keys(obj);
const reuslt = {};
keys.forEach(item => {
const changeKey = item.replace(/_/g, '');
if (typeof obj[item] === 'object') {
reuslt[changeKey] = reglarExpress(obj[item]);
} else {
reuslt[changeKey] = obj[item];
}
})
return reuslt;
}
const result = reglarExpress(a);
console.log('result', result);
"{\n "docKey":"partner.markdown.auth0",\n "stepID":"428537"\n}\n"
Solve:
function stringValFilter(str) {
let s = "";
if (str.length === 0) {
return "";
}
s = str.replace(/&/g, "&");
s = s.replace(/</g, "<");
s = s.replace(/>/g, ">");
s = s.replace(/ /g, " ");
s = s.replace(/'/g, "\'");
s = s.replace(/"/g, "\"");
s = s.replace(/\n/g, "");
return s;
}
给定一个非空整数数组,除了某个元素出现一次以为,其余每个元素均出现俩次。找出那个只出现了一次的元素
示例:
输入: [2,2,2,1]
输出: 1
**解决方案一
1.通过对象的方案
const a = [2, 2, 1, 1, 11, 6, 11,];
function test(a) {
const obj = {};
a.map(item => {
if (obj[item]) {
obj[item] = ++obj[item];
} else {
obj[item] = 1;
}
});
let value;
for (const key in obj) {
if (obj[key] === 1) {
value = key
}
}
return value;
}
**解决方案二
1.通过2层循环,获取到二维数组,每个元素里具体的数值都是相同的。
2.然后通过fliter(), 找出长度是1的数组,然后获取里面的元素值
function change(data) {
const filterArr = data.map(item => {
return data.filter((v) => {
return v === item;
})
});
let find;
filterArr.filter(item => {
if (item.length === 1) {
find = item[0];
}
});
return find;
}
思路:
.div1 {
width: 200px;
height: 200px;
background: #cccccc;
position: relative;
}
const ele = document.getElementsByTagName('div')[0];
const mouse = {
mouseLock: false,
mouseEleClient: {
top: 0,
left: 0
}
}
//鼠标点击的时候
ele.onmousedown = function (e) {
e = e || window.event;
var top = e.clientY;
var left = e.clientX;
mouse.mouseEleClient.top = top - this.offsetTop;
mouse.mouseEleClient.left = left - this.offsetLeft;
mouse.mouseLock = true;
}
//鼠标拖动的时候
ele.onmousemove = function (e) {
if (mouse.mouseLock) {
var top = e.clientY;
var left = e.clientX;
var divTop = top - mouse.mouseEleClient.top;
var divLeft = left - mouse.mouseEleClient.left;
ele.style.top = divTop + 'px';
ele.style.left = divLeft + 'px';
}
}
//鼠标放开的时候
ele.onmouseup = function (e) {
mouse.mouseLock = false;
}
const cal = function(nums){
if(nums == 0){return 0}
if(nums == 1){return 1 }
return cal(nums-1)+ cal(nums-2)
}
cal(6) //8
在开发过程中发现,各个浏览器上的Array.prototype.sort内部算法实现机制不一样,导致执行结果有偏差。
根据stackoverflow上的问答和维基百科上的结果,可以知道 chrome 目前采用快排(QuickSort)和插入排序(InsertaionSort),而对于火狐,它采用归并排序(MergeSort)。而IE使用快排。
直接看代码:
根据自己新定义的排序规则进行排序
const arr = [
{ code: "abb", name: "abb", age: 20 },
{ code: "bbb", name: "bbb", age: 40 },
{ code: "hhb", name: "hhb", age: 18 },
{ code: "yyb", name: "yyb", age: 10 },
{ code: "bab", name: "bab", age: 16 },
{ code: "ddb", name: "ddb", age: 15 }
];
arr.sort(function (a, b) {
return a.age - b.age
})
排序规则是根据一个number的值来进行排序的话,
各种浏览器可以保持相同的处理结果。
const arr = [
{ code: "abb", name: "abb", age: 20 },
{ code: "bbb", name: "bbb", age: 40 },
{ code: "hhb", name: "hhb", age: 18 },
{ code: "yyb", name: "yyb", age: 10 },
{ code: "bab", name: "bab", age: 16 },
{ code: "ddb", name: "ddb", age: 15 }
];
arr.sort(function (a, b) {
return a.code > b.code
})
排序规则是根据一个string的值来进行排序的话,
firefox和google等展示的结果不一样,就出现了兼容性问题
第一种
arr.sort(function (a, b) {
return a.code.localeCompare(b.code) || a.code > b.code
})
第二种:
//判断是否Firefox浏览器
const userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
arr.sort(function (a, b) {
if (userAgent.indexOf("Firefox") > -1) {
return a.code > b.code;
}
if (a.code < b.code) {
return -1
}else if(a.code === b.code){
return 0
}else{
return 1
}
})
function sum(numbers, target) {
const len = numbers.length;
const result = [];
for (let i = 0; i < len; i++) {
for (let j = 0; j < len - 1; j++) {
//数组中2个元素的值和目标元素值相同,并且数组中同一个元素不能使用2次
if (numbers[i] + numbers[j] === target ) {
result.push(i);
result.push(j);
return result;
}
}
}
}
const numbers = [1, 2, 3, 4, 5];
const target = 8;
console.log(sum(numbers, target)); //[2, 4]
function sum(numbers, target) {
const map = new Map();
const len = numbers.length;
for (let i = 0; i < len; i++) {
const a = target-numbers[i];
if(map.has(a)){
return [map.get(a),i]
}else{
map.set(numbers[i],i);
}
}
}
const numbers = [1, 2, 3, 4, 5];
const target = 8;
console.log(sum(numbers, target)); //[2, 4]
对于 JS 而言,我们面对的仅仅只是异常,异常的出现不会直接导致 JS 引擎崩溃,最多只会使当前执行的任务终止。
try-catch只能捕获到同步运行时的错误,对语法和异步错误无能为力,也捕获不到
语法错误:
try{
let name ="aa";
console.log(aaaaa);
}catch(e){
console.log("buhuo",e);
}
异步错误
try{
setTimeout(() => {
undefined.map((v)=>{
return v
})
}, 1000);
}catch(e){
console.log("buhuo",e);
}
可以捕获到运行是异常
window.onerror = function(message, source, lineno, colno, error) {
console.log('捕获到异常:',{message, source, lineno, colno, error});
}
let name = 'Jartto
window.onerror 函数只有在返回 true 的时候,异常才不会向上抛出,否则即使是知道异常的发生控制台还是会显示 Uncaught Error: xxxx
onerror 最好写在所有 JS 脚本的前面,否则有可能捕获不到错误;
onerror 无法捕获语法错误;
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.