自省系列JS-2

判断设备来源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 判断设备来源
*/
function deviceType() {
const ua = navigator.userAgent;
const agent = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
for(let i= 0, len = agent.length; i < len; i++ ) {
if(ua.indexOf(agent[i]) > 0) {
return agent[i];
}
}
}

/**
* 微信判断
*/
function isWx() {
const ua = navigator.userAgent.toLowerCase();
if(ua.match(/MicroMessenger/i) === 'micromessenger') {
return true;
}
return false;
}

arguments

  • arguments不是一个数组,是个类数组对象
  • [...]arguments,Array.from(arguments),[].slice.call(arguments)来转化为数组
  • 严格模式下禁止使用

代码解析

1
2
3
4
5
6
7
8
// $$('*') 为获取所有 dom 元素,返回数组
[].forEach.call($$("*"), function(element) {
// forEach 的回调函数,这里的 element 是数组中每个 dom 元素
element.style.outline =
// ~~是取整 1<<24 是位运算 结果为 16777216
// 之后的 toString(16) 为进行 16 进制的转换 即颜色
"1px solid #" + (~~(Math.random() * (1 << 24))).toString(16);
});

获取数组中最大值和最小值

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 获取数组中最大值和最小值
* @param {Array} arr
*/
function maxAndMinArr(arr) {
const min = Math.min.apply(null, arr);
const max = Math.max.apply(null, arr);

return {
max: max,
min: min,
};
}

this的理解

  • 当前运行环境的上下文
  • 全局this,是window
  • 函数中的this,指向调用者
  • 构造函数中的this,指向实例化之后的对象
  • call,apply,bind可以改变指向,指向第一个参数

返回空格分隔的单词串中,最长的单词

1
2
3
4
5
6
7
8
9
10
11
/**
* 返回以空格分隔的单词串中 最长的单词
* @param {String} str
*/
function maxLengthWord(str) {
return str.split(" ").reduce((pre,cur) => {
return pre.length > cur.length ? pre : cur;
})
}

console.log(maxLengthWord("hello world javascript")); //javascript

eval

  • 不安全的
  • 将传入的字符串当做 JavaScript 代码进行执行
  • 性能低,因为只有在执行eval时,才会调用JS解释器进行编译,无法经过编译器的优化
  • eval作用域混乱

跨域

  • 什么时候触发跨域

    不同源的时候会触发.浏览器同源策略指的是协议相同,host相同,端口相同

  • 为什么浏览器禁止跨域

    CSRF攻击:跨站请求伪造,攻击者盗用了你的身份,以你的名义发送恶意请求

    比如: 当你登录A网站,本地生成cookie,此时你又去访问B网站,B网站获取了A的cookie请求A网站.此时就等于你的账号相关权限B同样可以拥有访问

  • 跨域解决方案

    • JSONP: 通过script可以跨域的原理,请求接口,执行服务端的回调函数.但是只能是GET请求
    • iframe
    • form
    • CORS: 跨域资源共享,需要后端配合
    • nginx代理

IIFE的理解

  • 立即执行函数,函数创建后立即执行
  • 独立作用于,变量私有化,无法直接访问到,避免变量冲突
  • 避免全局作用域的污染

深拷贝

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
/**
* 判断是否为对象
* @param {*} value
*/
function isObjet(value) {
const type = typeof value;
return value !== null && (value === "object" || value === "function");
}

/**
* 深拷贝对象
* @param {*} target
*/
function deepClone(target) {
let result;
// 判断是否为对象
if (isObjet(target)) {
return target;
}
// 检查循环引用并返回其对应的拷贝
cache || (cache = new Map());
const cached = cache.get(value);
if (cached) {
return cached;
}

const isArr = Array.isArray(target);
// 如果是数组
if (isArr) {
for (let i = 0, len = target.length; i < len; i++) {
//循环数组 递归赋值
result[i] = deepClone(target[i]);
}
} else {
// 对象 获取属性 递归复制
Object.keys(Object(target)).forEach((key) => {
result[key] = deepClone(target[key]);
});
}
return result;
}

创建对象的方式

  • 字面量 {}
  • 构造函数创建: new Object()
  • Object.create()

事件冒泡

  • 事件执行顺序,捕获阶段->目标阶段->冒泡阶段.
  • 冒泡是指从事件源开始,一直向上传递,一直到document,如果父级也存在同样类型的事件时,也会被触发
  • 可以通过event.stopPropagation阻止冒泡

EventLoop

执行顺序

  • JS引擎有多个线程,但是同一时间只执行一个任务,其他任务会放到队列中.
  • JS执行时,遇到同步任务,放到主线程进行执行,遇到异步任务会放到异步队列,异步队列分为宏任务和微任务
  • 主线程任务执行完毕,从微任务队列中获取任务,放到主线程进行执行
  • 微任务队列完成后,再从宏任务获取任务,如果又遇到微任务,则放到微任务队列,当前宏任务完成后,获取队列中的微任务,所有微任务执行完成在继续获取宏任务执行.(不断循环)

宏任务

  • setTimeOut
  • setInterval
  • setImmediate

微任务

  • Promise

获取两个时间差

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
/**
* 获取两个时间差
* @param {Date} endAt
* @param {Date} startTime
*/
function diffDateTime(endAt, startTime = new Date()) {
const endTime = new Date(endAt);
//计算两个时间相差毫秒
const dateDiff = endTime.getTime() - startTime.getTime();
//天数
const dayDiff = Math.floor(dateDiff / (24 * 60 * 60 * 1000));

//小时 取模获取计算天数后剩余小时数的毫秒
const hourMs = dateDiff % (24 * 60 * 60 * 1000);
const hours = Math.floor(hourMs / (60 * 60 * 1000));

//分钟 取模获取经计算小时剩余分钟数的毫秒
const minutesMs = hourMs % (60 * 60 * 1000);
const minutes = Math.floor(minutesMs / (60 * 1000));

//秒 取模获取计算分钟后剩余秒的毫秒
const secondMs = minutesMs % (60 * 1000);
const seconds = Math.floor(secondMs / 1000);

return dayDiff + "天" + hours + "小时" + minutes + "分" + seconds + "秒";
}

console.log(diffDateTime("2020-05-12 17:05:00")); //0天0小时56分27秒

##求N的阶乘

1
const factorial = (num) => num === 1 ? num : num * factorial(num - 1);