JavaScript实战代码

2021/5/21 JavaScript

# 1. 如何判断元素是否在可视区域

# 方法一:

  1. 通过document.documentElement.clientHeight获取屏幕可视窗口高度
  2. 通过element.offsetTop获取元素相对于文档顶部的距离
  3. 通过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;
}
1
2
3
4
5
6
7

# 2. 如何判断对象是否为空

const obj = {};
Object.keys(obj).length === 0  // true 则为空对象
Object.keys(obj).length === 0 && obj.constructor === Object
1
2
3

为什么还要额外判断对象的constructor呢?即构造函数呢?

在 JavaScript 中,有9个内置构造函数。

new Object();
new String();
new Number();
new Boolean();
new Array();
new RegExp();
new Function();
new Date();
new Error();
1
2
3
4
5
6
7
8
9

我们可以使用这些构造函数来创建对象,例如:new Object()(日常不推荐使用)。

const obj = new Object();
Object.keys(obj).length === 0; // true
1
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
1
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
1
2
3
4
5
6
7
8
9
10
11
12

结果正确,实际上是我们对边界情况进行了处理。

Object.keys(null).length === 0;   // TypeError
Object.keys(undefined).length === 0;  // TypeError
1
2

总结:badEmptyCheckgoodEmptyCheck两个方法的区别主要在于添加了对边界情况的处理,所以后者更适用于公共组件和一些库上。

function goodEmptyCheck(value) {
    return value && Object.keys(value).length === 0 && value.constructor === Object;
}
1
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; //清空数组
1
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 5. 条件短路

if (hungry) {
    goToFridge();
}

// 进一步简化代码,同时使用变量和函
hungry && goToFridge()
1
2
3
4
5
6

# 6. 动态属性名称

const dynamic = 'flavour';
var item = {
    name: 'Coke',
    [dynamic]: 'Cherry'
}
console.log(item); 
// { name: "Coke", flavour: "Cherry" }
1
2
3
4
5
6
7

# 7. 一些屏蔽事件操作

  • 彻底屏蔽鼠标右键
oncontextmenu=”window.event.returnValue=false< table border oncontextmenu=return(false)>< td>no< /table> 可用于 Table
1
2
  • 取消选取、防止复制
< body onselectstart=return false>
    
// css
user-select: none;
1
2
3
4
  • JS 不允许粘贴
onpaste=return false
1
  • JS 防止复制
oncopy=return false;” oncut=return false;
1
  • 禁用输入法
< input style=”ime-mode:disabled”>
1
  • 防止被人 frame
if (top.location != self.location)top.location=self.location;
1
  • 网页禁用另存为
< no>< iframe src=*.html>< /iframe>< /no>
1

# 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)];
}
1
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;
1
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');
}
1
2
3
4
5
6
7
8

# 11. 过滤空值

使用 filter() 过滤 “空” 值,如 nullundefined 或空字符串,可以使用 .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);
})
1
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',
// }
1
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;
}
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;
}
1
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();
1
2
3
4
5
6

# 16. 格式化数字

formatNumber(number) {
    const f = new Intl.NumberFormat();
    return f.format(number);
}
1
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, '') : '';
}
1
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) // 递归
}
1
2
3
4
5
6
7
8
9

# 19. 等待一段时间再执行

const wait = async (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
1

# 20. 获取随机布尔值

const getRandomBoolean = () => Math.random() >= 0.5
1

# 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;
  }
}
1
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);    // 二进制转十六进制;也可以看做是二进制先转成十进制,再转成十六进制
1
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('');
}
1
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,'')" />
1
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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Last Updated: 2023/04/22, 23:39:26
彩虹
周杰伦