JavaScript实战代码
# 1. 如何判断元素是否在可视区域
# 方法一:
- 通过
document.documentElement.clientHeight
获取屏幕可视窗口高度 - 通过
element.offsetTop
获取元素相对于文档顶部的距离 - 通过
document.documentElement.scrollTop
获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
然后判断②-③<①是否成立,如果成立,元素就在可视区域内。
# 方法二:getBoundingClientRect
通过getBoundingClientRect()
方法来获取元素的大小以及位置。
假设const bound = el.getBoundingClientRect();
来表示图片到可视区域顶部距离;
并设 const clientHeight = window.innerHeight;
来表示可视区域的高度。
随着滚动条的向下滚动,bound.top
会越来越小,也就是图片到可视区域顶部的距离越来越小,当bound.top===clientHeight
时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。
也就是说,在bound.top<=clientHeight
时,图片是在可视区域内的。
function isInSight(el) {
const bound = el.getBoundingClientRect();
const clientHeight = window.innerHeight;
//如果只考虑向下滚动加载
//const clientWidth = window.innerWeight;
return bound.top <= clientHeight + 100;
}
2
3
4
5
6
7
# 2. 如何判断对象是否为空
const obj = {};
Object.keys(obj).length === 0 // true 则为空对象
Object.keys(obj).length === 0 && obj.constructor === Object
2
3
为什么还要额外判断对象的constructor
呢?即构造函数呢?
在 JavaScript 中,有9个内置构造函数。
new Object();
new String();
new Number();
new Boolean();
new Array();
new RegExp();
new Function();
new Date();
new Error();
2
3
4
5
6
7
8
9
我们可以使用这些构造函数来创建对象,例如:new Object()
(日常不推荐使用)。
const obj = new Object();
Object.keys(obj).length === 0; // true
2
平常使用的判断方法也没问题啊🤔🤔🤔
但是当使用剩下的内置构造函数创建就会出现问题。
function badEmptyCheck(value) {
return Object.keys(value).length === 0;
}
badEmptyCheck(new String()); // true
badEmptyCheck(new Number()); // true
badEmptyCheck(new Boolean()); // true
badEmptyCheck(new Array()); // true
badEmptyCheck(new RegExp()); // true
badEmptyCheck(new Function()); // true
badEmptyCheck(new Date()); // true
badEmptyCheck(new Error();); // true
2
3
4
5
6
7
8
9
10
11
12
是不是有点明白了?在添加构造函数判断后:
function goodEmptyCheck(value) {
return Object.keys(value).length === 0 && value.constructor === Object;
}
goodEmptyCheck(new String()); // false
goodEmptyCheck(new Number()); // false
goodEmptyCheck(new Boolean()); // false
goodEmptyCheck(new Array()); // false
goodEmptyCheck(new RegExp()); // false
goodEmptyCheck(new Function()); // false
goodEmptyCheck(new Date()); // false
goodEmptyCheck(new Error();); // false
2
3
4
5
6
7
8
9
10
11
12
结果正确,实际上是我们对边界情况进行了处理。
Object.keys(null).length === 0; // TypeError
Object.keys(undefined).length === 0; // TypeError
2
总结:badEmptyCheck
和goodEmptyCheck
两个方法的区别主要在于添加了对边界情况的处理,所以后者更适用于公共组件和一些库上。
function goodEmptyCheck(value) {
return value && Object.keys(value).length === 0 && value.constructor === Object;
}
2
3
# 3. 清空和截短数组--改变 length 属性
const arr = [1,2,3,4,5,6,7,8,9];
arr.length = 5;
console.log(arr); //[1,2,3,4,5]
arr.length = 0; //清空数组
2
3
4
# 4. 数组求和
var numbers = [3, 5, 7, 2];
var sum = numbers.reduce((x, y) => x + y);
console.log(sum); // 17
// 不使用循环,不使用标准库的函数
function sum (arr, i = 0) {
function f (i) {
if (i >= arr.length) {
return 0;
}
return arr[i] + f(i + 1);
}
return f(i);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 5. 条件短路
if (hungry) {
goToFridge();
}
// 进一步简化代码,同时使用变量和函
hungry && goToFridge()
2
3
4
5
6
# 6. 动态属性名称
const dynamic = 'flavour';
var item = {
name: 'Coke',
[dynamic]: 'Cherry'
}
console.log(item);
// { name: "Coke", flavour: "Cherry" }
2
3
4
5
6
7
# 7. 一些屏蔽事件操作
- 彻底屏蔽鼠标右键
oncontextmenu=”window.event.returnValue=false”
< table border oncontextmenu=return(false)>< td>no< /table> 可用于 Table
2
- 取消选取、防止复制
< body onselectstart=”return false”>
// css
user-select: none;
2
3
4
- JS 不允许粘贴
onpaste=”return false”
- JS 防止复制
oncopy=”return false;” oncut=”return false;”
- 禁用输入法
< input style=”ime-mode:disabled”>
- 防止被人 frame
if (top.location != self.location)top.location=self.location;
- 网页禁用另存为
< no>< iframe src=*.html>< /iframe>< /no>
# 8. 数组去重
// Hash:
function unique(arr = []) {
if (arr.length === 0 || arr.length === 1) {
return arr;
}
const newArray = [];
const hash = {};
for (let i = 0; i < arr.length; i++) {
if (!hash[arr[i]]) {
hash[arr[i]] = 1;
newArray.push(arr[i]);
}
}
return newArray;
}
// Set:
function unique(arr = []) {
if (arr.length === 0 || arr.length === 1) {
return arr;
}
return [...new Set(arr)];
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 9. 文件的读取
<input type='file' id='file' />
document.querySelector('#file').onchange = function(e) {
// const file = e.target.files[0];
const file = this.files[0];
const fr = new FileReader();
fr.readAsText(file);
fr.onload = function() {
const result = fr.result;
// 其它处理
const st = document.createElement('style');
st.innerHTML = result;
document.querySelector('head').appendChild(st);
}
}
// 清空,防止选择同样的文件不再触发 change 事件
<input type='file' ref='inputFile' />
this.$refs.inputFile.value = null;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 10. 网络状态检测
const state = window.navigator.onLine; // boolean
window.ononline = function() {
alert('good');
};
window.onoffline = function() {
alert('bad');
}
2
3
4
5
6
7
8
# 11. 过滤空值
使用 filter()
过滤 “空” 值,如 null
、undefined
或空字符串,可以使用 .filter(Boolean)
的缩写方法;它将所有空值转为 false
并从列表中删除它们,优雅!
const groceries = ['apple', null, 'milk', undefined, 'bread', ''];
const cleanList = groceries.filter(Boolean);
console.log(cleanList); // 'apple', 'milk', 'bread';
filter(Boolean);
filter((item, index) => {
return Boolean(item);
})
map(parseInt);
map((item, index) => {
return parseInt(item, index);
})
2
3
4
5
6
7
8
9
10
11
12
13
# 12. 箭头函数直接返回对象
使用箭头函数返回一个对象,为了和函数的 {
区分开来,在外层包一层 (
即可解决。
const createPerson = (age, name, nationality) => ({
age,
name,
nationality,
});
const caroline = createPerson(27, 'Caroline', 'US');
console.log(caroline);
// {
// age: 27,
// name: 'Caroline'
// nationality: 'US',
// }
2
3
4
5
6
7
8
9
10
11
12
13
# 13. 根据时间戳计算相差天数
function formatTime (value, format) {
let date = new Date(value);
let y = date.getFullYear();
let M = date.getMonth() + 1;
let d = date.getDate();
let h = date.getHours();
let m = date.getMinutes();
let s = date.getSeconds();
format = format
.replace('MM', this.s2d(M))
.toLowerCase()
.replace('yyyy', y)
.replace('yy', y % 100)
.replace('dd', this.s2d(d))
.replace('d', d)
.replace('hh', this.s2d(h))
.replace('h', h)
.replace('mm', this.s2d(m))
.replace('m', m)
.replace('ss', this.s2d(s))
.replace('s', s);
return format;
}
getDateDiff(+new Date(), timestamp)
getDateDiff(from, to) {
let currentFrom = formatTime(from, 'yyyy-MM-dd');
let currentTo = formatTime(to, 'yyyy-MM-dd');
let fromTime = new Date(Date.parse(currentFrom.replace(/-/g, '/'))).getTime();
let toTime = new Date(Date.parse(currentTo.replace(/-/g, '/'))).getTime();
let dates = Math.floor((toTime - fromTime)) / (1000 * 60 * 60 * 24);
return dates + 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
# 14. 深拷贝--循环递归
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== "object") return obj;
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 15. 分页优化
// 当删除当前页面最后一条数据的时候,应该请求上一页的数据并展示
// total pageSize currentPage
const totalPage = Math.ceil((this.total - 1) / this.pageSize);
this.currentPage = this.currentPage > totalPage ? totalPage : this.currentPage;
this.currentPage = this.currentPage < 1 ? 1 : this.currentPage;
this.updateInfo();
2
3
4
5
6
# 16. 格式化数字
formatNumber(number) {
const f = new Intl.NumberFormat();
return f.format(number);
}
2
3
4
# 17. 手写字符串 trim
String.prototype.trim = function () {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
}
const trim = (str) => {
return str ? str.replace(/(^\s*)|(\s*$)/g, '') : '';
}
2
3
4
5
6
7
# 18. 数组铺平
function flat(arr) {
// 验证 arr 中,还有没有深层数组 [1, 2, [3, 4]]
const isDeep = arr.some(item => item instanceof Array)
if (!isDeep) {
return arr // 已经是 flatern [1, 2, 3, 4]
}
const res = Array.prototype.concat.apply([], arr)
return flat(res) // 递归
}
2
3
4
5
6
7
8
9
# 19. 等待一段时间再执行
const wait = async (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
# 20. 获取随机布尔值
const getRandomBoolean = () => Math.random() >= 0.5
# 21. 检测鼠标是否在指定元素内
// 前提是要在鼠标触发事件中执行, 例如 mouseenter、mouseleave、mousemove 等等
checkIn (_element) {
// 指定元素 / _element
const ele = document.querySelector('.test');
// 鼠标的位置
const x = Number(window.event.clientX);
const y = Number(window.event.clientY);
// 元素的位置
const eleX = Number(ele.getBoundingClientRect().left);
const eleXWidth = Number(ele.getBoundingClientRect().left + ele.clientWidth);
const eleY = Number(ele.getBoundingClientRect().top);
const eleXHeight = Number(ele.getBoundingClientRect().top + ele.clientHeight);
if (x > eleX && x < eleXWidth && y > eleY && y < eleXHeight) {
return true;
} else {
return false;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 22. 十进制转二进制
var num = 10;
num.toString(2); // 十进制转二进制
num.toString(8); // 十进制转八进制
num.toString(10); // 十进制转十进制
num.toString(16); // 十进制转十六进制
parseInt(num, 2); // 二进制转十进制;num 被看做是二进制的数
parseInt(num, 8); // 八进制转十进制;num 被看做是八进制的数
parseInt(num, 16); // 十六进制转十进制;num 被看做是十六进制的数
parseInt(num, 2).toString(8); // 二进制转八进制;也可以看做是二进制先转成十进制,再转成八进制
parseInt(num, 2).toString(16); // 二进制转十六进制;也可以看做是二进制先转成十进制,再转成十六进制
2
3
4
5
6
7
8
9
10
11
12
13
// 自定义实现十进制转二进制
var numberToBinary = function(num) {
var result = [];
if (num < 0) {
return num;
}
// 使用 do while 解决 num 等于 0 的情况
do {
var temp = num % 2;
temp == 0 ? result.push('0') : result.push('1');
num = Math.floor(num / 2);
} while(num != 0);
// 反转数组
result.reverse();
return result.join('');
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 23. 输入框事件
// 不允许有空格、中文
<input type="text" oninput="value=value.replace(/[\u4e00-\u9fa5]|(^\s+)|(\s+$)/ig,'')" />
// 键盘仅支持输入数字
<input type="text" onkeyup="value=value.replace(/[^\d]/ig,'')" />
2
3
4
5
# 24. 字符串复制
const copy = () => {
navigator && navigator.clipboard && navigator.clipboard.writeText(value).then(_res => {
// copy success
});
}
const copy = () => {
const input = document.createElement('input');
document.body.appendChild(input);
input.value = value;
input.select();
document.execCommand('Copy');
document.body.removeChild(input);
// copy success
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15