👉 前言
在前端开发中,无论是否使用框架,在代码编写上,都与 Javascript
息息相关。更何况是在 框架(基于 Js 搭建的框架) 盛行的当下,以 Vue框架为例。
Vue
有着使 HTML和JS直接关联的方法,也就是 Vue指令
。比如: v-if、v-for、v-bind、v-model
等等,其中都多少和Javascript
相关联,并且有些指令,甚至能够使 Javascript代码直接穿插在 HTML中。
所以学习 Javascript 就显得尤为重要了,接下来,小温将给大家带来,Javascript 相关的一些知识点及小技巧!
提示:以下是本篇文章正文内容,下面案例可供参考
👉 学习目的 及 版本
> 学习目的:
- 减少
LOC
(代码行数)的数量,提高代码质量; - 降低代码时间/空间复杂度;
- 灵活使用
Javascript
中小技巧,降低开发所需时间; - 适用于
编程比赛
、黑客马拉松
、限时任务
,使代码看起来更加简洁高效; - 当然是为了征服 Javascript 啦 🤣
> 版本
本文中,大多数 JavaScript Hacks
使用 ECMAScript6 (ES2015/ES6)
以后的技术,尽管最新版本是 ECMAScript11(ES2020)
。
接下来,废话不多说,进入正题!
👉 Javascript 技巧
正文阶段:内容中所有 案例代码均在 Google Chrome 的控制台 进行过测试,运行无误! 放心 “ 食用 ”
⌨️ 1、声明和初始化数组
使用 数组对象 Array自带的填充函数 fill(每个项填充的内容) 来填充数组。
> 语法
const array = Array( integer ).fill(itemVal);
/*
integer 为所要声明的数组长度
itemVal 为声明的数组每项的初始值
可以配合 数组遍历函数 map 来声明 N维数组
语法如下:
*/
const array = Array( integer_1 ).fill(itemVal_1).map(item => {
// 可以添加一些需要逻辑处理的操作
return Array( integer_2 ).fill(itemVal_2)
});
> 执行案例
💡 Tips:建议上手试试喔,几行代码。好记性不如烂笔头,多动手,记得牢!
⌨️ 2、数组求和、求最值(最大 / 最小)、扁平化多维数组
使用 数组对象 Array自带的函数 reduce(),接收一个函数作为累加器,进行逻辑判断,处理数据中的项。
> 语法
array.reduce((pre, cur, index, arr)=>{
...
}, init);
- pre: 必需。初始值(
当设定了初始化时,返回为初始值
), 或者计算结束后的返回值(sum
)。 - cur: 必需。当前元素。
- index: 可选。当前元素的索引。
- arr: 可选。当前元素所属的数组对象。
- init: 可选。传递给函数的初始值,相当于pre的初始值。
注意:reduce里面有一定要return,return出去的值也要注意。
具体使用 👉 点击跳转 大佬文章,讲解内容偏多,只讲常用的,讲复杂了,大家也不好吸收 😁。
> 执行案例
案例一:数组求和
const total = arr.reduce((sum, item) => {
return sum + item
}, 0);
案例二:数组求最值 - 最大 / 最小
// 求数组中的最大值 arr[max]
const max = arr.reduce((a, b) => a > b ? a : b );
// 求数组中的最小值 arr[min]
const min = arr.reduce((a, b) => a < b ? a : b );
案例三:数组扁平化多维数组
const total = arr.reduce((newArr, itemArr) => {
return newArr.concat(itemArr)
}, []);
> 执行效果
💡 Tips:建议上手试试喔,几行代码。好记性不如烂笔头,多动手,记得牢!
⌨️ 3、对字符串、数字或对象数组进行排序
JavaScript中数组排序的方法有两个reverse()和sort()。
- reverse()方法会反转数组项的顺序。
- sort()方法会按照字符串升序排列数组项。
注意:
sort()
方法会调用每个数组项的tostring()方法,即使数组中的每一项都是数值,sort()
方法比较的也是字符串。所以为了弥补
sort()
无法比较数值的缺陷,允许接收一个比较函数作为参数。
比较函数接收由原数组
依次循环传递连续的两个数组
项作为比较函数的两个参数。规则如下:
- 如果第一个参数应该位于第二个参数
之前
,返回一个负数,相当于返回值为false
时,不调换
它们的位置。 - 如果两个参数相等,则返回
0
,0
也相当于false
。不调换
它们的位置。 - 如果第一个参数应该位于第二个参数之后,返回一个正数,正数为一个
true
值,调换
它们的位置。
具体理解,可以看下面的案例,可以配合案例理解规则
> 实际案例
① 排序字符串数组
const stringArr = ["Joe", "Kapil", "Steve", "Musk"]
// 按照字符串比较, 字符串对应的编码值
stringArr.sort();
// 输出
(4) ["Joe", "Kapil", "Musk", "Steve"]
// 此处是以 sort 转换过的值,再倒序
stringArr.reverse();
// 输出
(4) ["Steve", "Musk", "Kapil", "Joe"]
② 排序数字数组
const array = [40, 100, 1, 5, 25, 10];
// 这里匹配到了最开头讲述的 sort 接受比较函数的处理规则
// ----升序----
array.sort((a,b) => a-b);
// 等价于 a - b > 0, 详细解释往下看
array.sort((a,b) => a > b);
// 输出
(6) [1, 5, 10, 25, 40, 100]
// ----降序----
array.sort((a,b) => b-a);
// 等价于
array.sort((a,b) => b > a);
// 输出
(6) [100, 40, 25, 10, 5, 1]
③ 对象数组排序
原理其实都差不多,根据 sort
接受比较函数,函数的返回值来确定,是否调换位置,返回值会被当成一个布尔值来判断,控制是否调换。
比如, 上文中的 “ a - b
”, 如果循环中, a - b > 0
,使用不等式转换一下,为 a > b
, 即 判断 a是否大于b , 若大于,则为 true
。为true
,则调换位置,反之,为false
则不变。
sort函数相当于 自动执行了 设定 判断规则的 冒泡排序
,循环对数组中两个连续的项进行设定规则校验。
const objectArr = [
{
first_name: 'Lazslo', last_name: 'Jamf' },
{
first_name: 'Pig', last_name: 'Bodine' },
{
first_name: 'Pirate', last_name: 'Prentice' }
];
objectArr.sort((a, b) => a.last_name.localeCompare(b.last_name));
// 输出
(3) [{
…}, {
…}, {
…}]
0: {
first_name: "Pig", last_name: "Bodine"}
1: {
first_name: "Lazslo", last_name: "Jamf"}
2: {
first_name: "Pirate", last_name: "Prentice"}
length: 3
💡 Tips:建议上手试试喔,几行代码。好记性不如烂笔头,多动手,记得牢!
⌨️ 4、从数组中过滤出虚假值
使用 数组对象 Array自带的函数 filter()
,函数返回一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
同时,该函数接收一个参数,参数可以是方法,但方法必须返回布尔值。若返回 true
,则将对应的数组项返回到新数组中, false
,则不返回!
> 语法
const newArr = array.filter((currentValue, index, arr)=>{
...
}, thisVal);
function
:整体必须传一个函数,或者其他替代值currentValue
: 必需。当前元素的值。index
: 可选。当前元素的索引。arr
: 可选。当前元素所属的数组对象。thisVal
: 可选。该对象作为执行回调时使用,传递给函数,作为 “ this ”的值。如果省略 “thisVal
”,this
值为undefined
。
❗ 注意
filter()
不会对空数组进行检测;filter()
不会改变原始数组。
具体使用方法,可以跳转到我之前 《Js 技巧 数组篇》,在此不多做赘述!
接下来看特殊栗子 🌰 -- 去除数组中的虚假值
> 执行案例
const array = [3, 0, 6, 7, '', false];
array.filter(Boolean);
// 等价于
array.filter(item => item);
// 将会判断 数组中的每一项,是否在转变为布尔值的时候,为 true,从而起到去除虚假值的效果
// 输出
(3) [3, 6, 7]
由于比较简单,就不做效果图展示啦,大伙动动手, 自己测试下吧!
⌨️ 5、灵活运用 Javascript 中的逻辑运算符
逻辑运算符通常用来组合多个表达式,逻辑运算符的运算结果是一个布尔值,只能有两种结果, true / false。
下表中列举了 JavaScript 中支持的逻辑运算符:
| 运算符 | 名称 | 示例 |
|--|--|--|
| && | 逻辑与 | x && y 表示如果 x 和 y 都为真,则为真 |
| II | 逻辑或 | x II y 表示如果 x 或 y 有一个为真,则为真 |
| ! | 逻辑非 | !x 表示如果 x 不为真,则为真 |
如果你想减少嵌套 if…else 或 switch case,你可以简单地使用基本的逻辑运算符AND/OR。
> 逻辑与运算(AND)
逻辑与运算(&&)
是 AND
布尔操作。只有两个操作数都为 true
时,才返回 true
,否则返回 false
。
let user; //定义变量,为空,转换为布尔值,为 false
let msg = ( !user && console.log("没有赋值") );
// 返回提示信息“没有赋值”, 并且msg 值为 undefined。原因是 console.log() 为一个函数,无返回值。
// ----- 等效于 -----
let user; //定义变量
if ( !user ){
//条件判断
console.log("变量没有赋值");
}
实际运行 -- 基于谷歌浏览器控制台
// ① 情景一
let user; //定义变量,为空,转换为布尔值,为 false
let msg = ( !user && console.log("没有赋值") );
// 输出 -> 没有赋值
console.log(msg)
// 输出 -> undefined
// ② 情景二
let user;
let msg = ( !user && false );
console.log(msg)
// 输出 -> false
// ③ 情景三
let user;
let msg = ( !user && (() => {
console.log('方法内输出');
return false;
}));
console.log(msg)
/* 输出
() => {
console.log('方法内输出');
return false;
}
*/
// ④ 情景四
let user;
let msg = ( !user && (() => {
console.log('方法内输出');
return true;
}));
console.log(msg)
/* 输出
() => {
console.log('方法内输出');
return true;
}
*/
// ⑤ 情景五
let user = 0;
let msg = ( user && (() => {
console.log('方法内输出');
return true;
}));
console.log(msg)
// 输出 undefined
如果变量 user 的值为 0
或 空字符串
等假值转换为布尔值
时,则为 false,那么当变量赋值之后,依然提示变量没有赋值
。因此,在设计时必须确保逻辑与左侧的表达式返回值是一个可以预测的值。
> 逻辑或运算(OR)
逻辑或运算||,是布尔值 OR
操作。如果两个操作数都为 true
,或者其中一个为 true
,就返回 true
,否则就返回 false
。
这里需要注意的一点是:逻辑或也是一种短路逻辑
,如果左侧表达式为 true
,则直接短路返回结果,不再运算右侧表达式
。运算逻辑如下:
第
1
步:计算第一个操作数(左侧表达式)的值。第
2
步:检测第一个操作数的值。如果左侧表达式的值可转换为 true,那么就会结束运算,直接返回第一个操作数的值。第
3
步:如果第一个操作数可以转换为 false,则计算第二个操作数(右侧表达式)的值。- 第
4
步:返回第二个操作数的值。
使用案例
let personArr = [
{
name: null,
age: 18,
youth: '未成年'
},
{
name: '张三',
age: 25,
youth: ''
}
]
personArr.forEach(item => {
item.youth = item.youth || ( item.age >= 18 ? '成年' : '未成年' )
item.name = item.name || '匿名'
/*
其实这里可以用三元来写, 或者是if来判断,但是最为简洁就是 ||。
下面写一下,等价于:
*/
item.name = item.name ? item.name : '匿名'
if(!item.name) {
item.name = '匿名'
}
});
其实逻辑运算符还能有许多混合运用方法,但是大致语法就是上面描述的了! 具体还得根据实际情景来运用! 大伙可以去控制台练练手....
逻辑与和逻辑或运算符具有以下 2 个特点:
在逻辑运算过程中,临时把操作数转换为布尔值,然后根据布尔值决定下一步的操作,但是不会影响操作数的类型和最后返回结果。
受控于第一个操作数,可能不会执行第二个操作数。
> 逻辑非运算(NOT)
逻辑非运算 !
,是布尔取反操作(NOT
)。作为一元运算符,直接放在操作数之前,把操作数的值转换为布尔值
,然后取反并返回。
逻辑非运算相对较为简单,直接上案例。
实际案例
console.log( ! {
} ); //如果操作数是对象,则返回false
console.log( ! 0 ); //如果操作数是0,则返回true
console.log( ! (n = 5)); //如果操作数是非零的任何数字,则返回false
console.log( ! null ); //如果操作数是null,则返回true
console.log( ! NaN ); //如果操作数是NaN,则返回true
console.log( ! Infinity ); //如果操作数是Infinity,则返回false
console.log( ! ( - Infinity )); //如果操作数是-Infinity,则返回false
console.log( ! undefined ); //如果操作数是undefined,则返回true
tips: 当对某一内容或者表达式进行两次逻辑非运算时,就相当于把操作数转换为布尔值。
⌨️ 6、删除重复值(技巧)
在开发中,我们时常会遇到一些需要去重数据的情景。
正常情况下,我们可以将 indexOf()
与 for
循环一起使用,该循环返回第一个找到的索引。或者可以用 较新的 includes()
从数组中返回布尔值 true / false
以找出/删除重复项。
但是为了提高效率,单纯用for + indexof() / includes(),显然不太简洁,也不高效。 不妨看看以下两种方法:
const array = [5,4,7,8,9,2,7,5];
// 利用 filter 函数 和 indexof() 直接筛选 数据
const newArr = array.filter((item, index, arr) => arr.indexOf(item) === index);
// 或者用 ES6 中新出的 Set对象,利用其特性,进行数组去重
const nonUnique = [...new Set(array)];
// 输出: [5, 4, 7, 8, 9, 2]
⌨️ 7、创建计数器对象 或 映射
在处理数据的时候,有些数据需要记录一些数据的出现次数(类似某些日志数据、状态出现次数等等
)。这时,我们就需要用到一个计数器对象
,来计算各个数据类型出现次数。正常情况下,计数器往往会被封装成一个公共方法(如果频繁使用的话)。 案例如下:
- 方法一: 利用循环 + 对象 记录
- 方法二: 利用循环 + Map 记录
const logData = [
{
id: '001',
name: '张三',
type: '借出书籍'
},
{
id: '001',
name: '张三',
type: '归还书籍'
},
{
id: '002',
name: '李四',
type: '借出书籍'
},
{
id: '002',
name: '李四',
type: '借出书籍'
}
];
// 方法一
const countObj_1 = (data, idName_1, idName_2) => {
const obj = {
};
data.forEach(item => {
obj[item[idName_1] + '-' + item[idName_2]] = obj[item[idName_1] + '-' + item[idName_2]] + 1 || 1
})
return obj;
};
// 方法二
const countObj_2 = (data, idName_1, idName_2) => {
const countMap = new Map();
data.forEach(item => {
countMap.set(item[idName_1] + '-' + item[idName_2], (
countMap.has(item[idName_1] + '-' + item[idName_2]) ?
countMap.get(item[idName_1] + '-' + item[idName_2]) + 1 : 1
))
})
return countMap ;
};
console.log('方法一', countObj_1(logData, 'id', 'type'))
console.log('方法二', countObj_2(logData, 'id', 'type'))
// 输出结果如下图
大致思路如上,能够通过这两种方法,去统计某种类型,某种数据出现的次数。希望大家灵活使用呀!
⌨️ 8、三元运算符
三元运算符,也称条件运算符。能够代替 if...else...,减少if语句的使用。但是在使用三元运算符的时候,一般不建议深层嵌套
。
📃 知识拓展
①一元运算符
:只需要一个数据就可以进行操作的运算符。例如:取反!、自增++、自减--;
②二元运算符
:需要两个数据就可以进行操作的运算符。例如:加法+、赋值=;
③三元运算符
:需要三个数据就可以进行操作的运算符。
// 语法格式: 变量名称 = 条件判断 ? 表达式A : 表达式B;
function grade(achievement) {
return achievement >= 90 ? '成绩优秀!'
: achievement >= 75 ? '成绩良好!'
: achievement >= 60 ? '成绩及格!'
: '成绩不及格!';
}
// 输出
grade(97): "成绩优秀!"
grade(85): "成绩良好!"
grade(65): "成绩及格!"
grade(55): "成绩不及格!"
⌨️ 9、JavaScript 中 循环语句
在 JavaScript
中,我们时常需要实现重复的对某些事件逻辑进行操作。而这种重复的工作,在js中 ,能够使用 循环语句实现。
📃 知识拓展
① 在ECMAScript5
(简称ES5
)中,有三种 for 循环,分别是:·for
、for-in
、forEach
。
② 在2015年6月份发布的ECMAScript6
(简称ES6
)中,新增了一种循环,是:for-of
循环语句的种类:
for
: 循环代码块一定的次数for...in...
: 循环遍历对象的属性while
: 当指定的条件为 true 时循环指定的代码块do/while
: 同样当指定的条件为 true 时循环指定的代码块for...of...
: 支持循环变量多种数据类型,如:字符串、Map、Set、数组等。且可以正确响应break
、continue
和return
语句,但不支持遍历普通对象。
🔖 语法案例
① for循环
// for 循环
for (语句 1; 语句 2; 语句 3)
{
被执行的代码块
}
// 语句 1 (代码块)开始前执行
// 语句 2 定义运行循环(代码块)的条件
// 语句 3 在循环(代码块)已被执行之后执行
for (let i=0; i < 5; i++)
{
x=x + "该数字为 " + i + "<br>";
}
② for...in 循环
// for...in 循环
for (key in Obj)
{
被执行的代码块
}
// key 循环遍历 对象的属性名
// Obj 被遍历的对象
let txt = '';
let person={
fname:"Bill",lname:"Gates",age:56};
for (x in person) // x 为属性名
{
txt=txt + person[x];
}
for-in
缺点 : 它不仅遍历数组中的元素,还会遍历自定义的属性,甚至原型链上的属性都被访问到。而且,遍历数组元素的顺序可能是随机的。
③ while / do...while 循环
// while 循环
while (条件)
{
需要执行的代码
}
// do...while 循环, 无论条件是否满足,必定先执行一次
do
{
需要执行的代码
}
while (条件);
while (i<5)
{
x=x + "The number is " + i + "<br>";
i++;
}
do
{
x=x + "The number is " + i + "<br>";
i++;
}
while (i<5);
④ forEach 循环 - ES5引入新循环
// for 循环
Array.forEach ((item, index, arr) => {
被执行的代码块
})
// item 遍历的数组项
// index 所遍历的数组下标
// arr 遍历的数组本身
// 添加数组当前项的索引参数,注意callback 函数中的三个参数顺序是固定的,不可以调整。
// 案例一
const arr = [1, 2, 3];
arr.forEach((item, index) => {
// 可以在这里面对 arr循环的 item项 修改
console.log(item, index);
});
// 输出结果如下
// 1, 0 // 2, 1 // 3, 2
// 案例二
const arr = [];
arr[0] = "a";
arr[3] = "b";
arr[10] = "c";
arr.name = "Hello world";
arr.forEach((data, index, array) => {
console.log(data, index, array);
});
// 输出结果如下:
/*
a 0 ["a", empty × 2, "b", empty × 6, "c", name: "Hello world"]
b 3 ["a", empty × 2, "b", empty × 6, "c", name: "Hello world"]
c 10 ["a", empty × 2, "b", empty × 6, "c", name: "Hello world"]
*/
📑 通过上述案例可见,
Array
在Javascript
中是一个对象,Array
的索引是属性名,与对象类型一致,默认是数字类型顺序。
上述案例中,这里的 index
是 Number
类型,并且也不会像 for-in
一样遍历原型链上的属性。
所以,使用 forEach 时,我们不需要专门地声明 index 和遍历的元素,因为这些都作为回调函数的参数。
另外,forEach 将会遍历数组中的所有元素,但是 ES5 定义了一些其他有用的方法,下面是一部分:
every
: 循环在第一次return false
后返回some
: 循环在第一次return true
后返回filter
: 返回一个新的数组,该数组内的元素满足回调函数map
: 将原数组中的元素处理后再返回reduce
: 对数组中的元素依次处理,将上次处理结果作为下次处理的输入,最后得到最终结果。
⑤ for...of 循环
// for...of 循环
for (item of arr)
{
被执行的代码块
}
// item 循环遍历 数组项
// arr 被遍历的数组
let arr = ['a', 'b', 'c'];
for (item of arr ) // x 为属性名
{
console.log(item)
}
// 输出结果如下
// a
// b
// c
for...of 的优点:
① 跟forEach
相比,可以正确响应break, continue, return
。
②for-of
循环不仅支持数组,还支持大多数类数组对象,例如 DOM nodelist 对象。
③for-of
循环也支持字符串遍历,它将字符串视为一系列 Unicode 字符来进行遍历。
④for-of
也支持Map
和Set
(两者均为 ES6 中新增的类型)对象遍历。
⌨️ 10、合并多个对象
实现原理: 利用 ...
Array / Object, 展开语法(Spread syntax
), 可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。
将所要合并的数组 / 对象,在新创建的数组 / 对象中展开。
注: 字面量一般指 [1, 2, 3] 或者 {name: "mdn"} 这种简洁的构造方式,只能展开表面的一层,如: [a,b,{ c: 1 }] ,展开成 a,b { c: 1 }。
// 语法格式: 变量名称 = 条件判断 ? 表达式A : 表达式B;
const person_PC = [
{
name: '李四',
age: 18
...
},
{
name: '张三',
age: 18
...
}
]
const person_MC = [
{
name: '王五',
age: 18
...
}
]
// 获取信息
const info = {
};
info.nameList = [ ...person_PC.map( item => item.name ), ...person_MC.map( item => item.name )]
console.log(info.nameList);
// 输出
[ '张三', '李四', '王五' ]
⌨️ 11、箭头函数
箭头函数表达式
是传统函数表达式的紧凑替代品
,有其局限性( 需要考虑this
指向问题 ),不能在所有情况下使用。
// 语法, 当只有一个参数时,括号可省略
funtionName = (val) => {
方法内容 };
let hello = (val) => {
return "Hello" + val;
}
// 等价于
let hello = val => "Hello" + val;
// 传统
const person = {
name: 'Kapil',
sayName() {
return this.name;
}
}
person.sayName();
// 输出
"Kapil", 可见, 不用箭头函数,传统的函数中,this的指向是 指向方法体内部,或者说,是其本身。
// 箭头函数
const person = {
name: 'Kapil',
sayName : () => {
return this.name;
}
}
person.sayName();
// 输出
"",可见,当前this指向,是指向整个外部。
/*
就比如,在vue中,我们有时候需要在某些地方,要自定义方法体。
但是会发现,使用传统的方法体时,会导致this无法正确的指向 new Vue() 中,而是指向定义的方法体本身。
这时,就要用到箭头函数了, 不会改变this原先的指向。
*/
💬 小温有话说
暂时更新至此....内容较多,请大家稍安勿躁哈,后续将持续更新此文章! 能看到这里的,相信大家都非常有耐心学习的! 希望大伙给小温点时间编写呀
参考文献
本文编写思路参考自 华为云 - 《提高代码效率的 20 个JavaScript 技巧和窍门》 ,作者:海拥。
往期内容 💨
🔥 < elementUi 中 树状侧边栏,机构单位 - 岗位 字典 >
🔥 < 了解 HTTP 这一篇就够了 :什么是 HTTP ?HTTP 和 HTTPS 有什么区别 ? >
🔥 <Javascript技巧: Javascript 是个难泡的妞,学点技巧征服 “ 她 ” >
结合其他优秀的文章 及 个人见解编写! 若有遗漏或者错误没写好的地方,还请见谅! 接受评论 / 私聊 指出问题嗷! 如果觉得写得还不错,麻烦点赞支持一下小温啦!🥰