A - C 系列
1、+ 号的隐式类型转换使用
1 + [ 3 ] //3
2 + [ 1 , 2 , 3 ] . slice ( - 1 ) //将 [3] 转换为了3
2、日期的转换
1 const addDaysToDate = ( date , n ) => {
2 const d = new Date ( date ) ;
3 d . setDate ( d . getDate ( ) + n ) ;
4 return d . toISOString ( ) . split ( 'T' ) [ 0 ] ;
5 } ;
6 addDaysToDate ( '2020-10-15' , 10 ) ; // '2020-10-25'
平时做日期的计算可能更多的使用时间戳,秒数去加减,其实有对应的 setDate,getDate等方法去做(月份也一样)
toISOString 可以将日期对象转换为 YYYY-MM-DDTHH:mm:ss.sssZ 格式,分割掉 T 字符,即可以得到日期形式
3、对象的合并
1 Object . assign
2 // 在像某对象添加多个属性时,可以直接采取合并方式
3 const addStyles = ( el , styles ) => Object . assign ( el . style , styles )
4 addStyles ( document . getElementById ( 'my-element' ) , {
5 background : 'red' ,
6 color : '#ffff00' ,
7 fontSize : '3rem'
8 } )
4、Array.from
1 3 5
6 let a = Array . from ( { length : 10 } , ( _ , i ) => i )
7 a // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5、关于工具函数封装tips
在封装工具类函数时,可以往闭包的角度靠(内部再返回一个函数)
有时候可以具有更好的拓展性
1 // 栗子:获取某些数中的最大值
2 const ary = ( fn , n ) => ( ... arg ) => fn ( ... arg . slice ( 0 , n ) )
3 const firstTwoMax = ary ( Math . max , 2 )
4 firstTwoMax ( 1 , 2 , 3 , 4 )
5
6 // ary的函数将传入的函数,作为返回函数的返回值调用,类似于将传入的函数缓存下来,再一次调用时,传入比较值
7 // 这样有个好处是,其实我们可以更改比较条件,比如 取最小值等等
8 // 这样工具函数更具有灵活性
6、 按条件分割数组
1 const bifurcateBy = ( arr , fn ) =>
2 arr . reduce ( ( acc , val , i ) => ( acc [ fn ( val , i ) ? 0 : 1 ] . push ( val ) , acc ) , [
3 [ ] ,
4 [ ] ,
5 ] )
6 bifurcateBy ( [ 'beep' , 'boop' , 'foo' , 'bar' ] , x => x [ 0 ] === 'b' ) ;
7 // [ ['beep', 'boop', 'bar'], ['foo'] ]
8
9 12
7、二分搜索算法,来查找已排序数组中某元素的位置
尽管现在有api可以直接实现位置查找,不过这个算法思想还是可以的
1 const binarySearch = ( arr , item ) => {
2 // 获取首位下标
3 let l = 0 ,
4 r = arr . length - 1 ;
5 while ( l <= r ) {
6 // 找到中间下标
7 const middle = Math . floor ( ( l + r ) / 2 )
8 const guess = arr [ middle ]
9 // 中间值与目标值比较
10 if ( guess == item ) return middle
11 // 中间值 > 目标值 ? 说明目标值在左侧,则取左边中间值再比较,反之亦然
12 guess > item ? r = middle - 1 : l = middle + 1
13 }
14 return - 1
15 }
16 binarySearch ( [ 1 , 2 , 3 , 4 ] , 4 ) // 3
二分搜索算法,采取折中的方式,将中间值与目标做比较,然后不断修改中间值,进而找到目标值
8、冒泡排序
1 const bubbleSort = arr => {
2 let swapped = false ;
3 const a = [ ... arr ] ;
4 for ( let i = 1 ; i < a . length ; i ++ ) {
5 swapped = false ;
6 for ( let j = 0 ; j < a . length - i ; j ++ ) {
7 if ( a [ j + 1 ] < a [ j ] ) {
8 // es6 变量交换
9 [ a [ j ] , a [ j + 1 ] ] = [ a [ j + 1 ] , a [ j ] ] ;
10 swapped = true ;
11 }
12 }
13 if ( ! swapped ) return a ;
14 }
15 return a ;
16 } ;
核心思想是将当前值a 与 下一位置值b比较,如果 a > b,就把 a和b交互位置
同时,一轮排序其实就可以实现最大值排最后了,所以循环次数越往后会越少,这也是 i < a.length ,j < a.length - i 的原因
es6 的变量交换位置,更加方便了,其实也可以用于多个变量的赋值改变类似: [count, i] = [count + 1, r + 1]...
9、将字符串首字母转换为大写
1 // 有意思的是 这里只是通过解构的方式,就把字符串的首字符与剩余字符分开了
2 // 在此之前你说否还想着截取首字母再来转换?
3 const capitalize = ( [ first , ... rest ] , lowerRest = false ) =>
4 first . toUpperCase ( ) +
5 ( lowerRest ? rest . join ( '' ) . toLowerCase ( ) : rest . join ( '' ) ) ;
6
7 capitalize ( 'fooBar' , true ) ; // 'Foobar'
10、链式控制异步流程
1 const chainAsync = fns => {
2 let curr = 0 ;
3 const last = fns [ fns . length - 1 ] ;
4 // 内部实现一个函数,用于逐次从数组中提取执行函数
5 const next = ( ) => {
6 const fn = fns [ curr ++ ] ;
7 // 提取函数与最后一个做对比,是否为最后一个函数
8 // 同时将这个内部函数,作为回调,传入下一个 异步函数中
9 fn === last ? fn ( ) : fn ( next ) ;
10 } ;
11 next ( ) ;
12 } ;
13
14 chainAsync ( [
15 next => {
16 console . log ( '0 seconds' ) ;
17 // 函数执行后,再执行传入的回调next
18 setTimeout ( next , 1000 ) ;
19 } ,
20 next => {
21 console . log ( '1 second' ) ;
22 setTimeout ( next , 1000 ) ;
23 } ,
24 ( ) => {
25 console . log ( '2 second' ) ;
26 }
27 ] ) ;
异步的实现,其实还是回调的使用,只不过封装后,看似同步
好比 Promise的resolve,获取结果后,通过resolve回调出去
Promise + async ,运用迭代器,使得可以控制函数的暂停和恢复,更加具有同步风范
11、js 实现一个剪切板
1 const copyToClipboard = str => {
2 const el = document . createElement ( 'textarea' ) ;
3 el . value = str ;
4 el . setAttribute ( 'readonly' , '' ) ;
5 el . style . position = 'absolute' ;
6 el . style . left = '-9999px' ;
7 document . body . appendChild ( el ) ;
8 const selected =
9 document . getSelection ( ) . rangeCount > 0
10 ? document . getSelection ( ) . getRangeAt ( 0 )
11 : false ;
12 el . select ( ) ;
13 document . execCommand ( 'copy' ) ;
14 document . body . removeChild ( el ) ;
15 if ( selected ) {
16 document . getSelection ( ) . removeAllRanges ( ) ;
17 document . getSelection ( ) . addRange ( selected ) ;
18 }
19 }
20
21 copyToClipboard ( 'Lorem ipsum' )
12、根据给定函数或者属性,统计符合条件的数组元素的个数
1 const countBy = ( arr , fn ) =>
2 arr . map ( typeof fn === 'function' ? fn : val => val [ fn ] ) . reduce ( ( acc , val ) => {
3 acc [ val ] = ( acc [ val ] || 0 ) + 1 ;
4 return acc ;
5 } , { } ) ;
6
7 countBy ( [ 6.1 , 4.2 , 6.3 ] , Math . floor ) ; // {4: 1, 6: 2}
8 countBy ( [ 'one' , 'two' , 'three' ] , 'length' ) ; // {3: 2, 5: 1}
9 countBy ( [ { count : 5 } , { count : 10 } , { count : 5 } ] , x => x . count )
13、实现一个发布订阅模式
1 const createEventHub = ( ) => ( {
2 hub : Object . create ( null ) ,
3 emit ( event , data ) {
4 ( this . hub [ event ] || [ ] ) . forEach ( handler => handler ( data ) ) ;
5 } ,
6 on ( event , handler ) {
7 if ( ! this . hub [ event ] ) this . hub [ event ] = [ ] ;
8 this . hub [ event ] . push ( handler ) ;
9 } ,
10 off ( event , handler ) {
11 const i = ( this . hub [ event ] || [ ] ) . findIndex ( h => h === handler ) ;
12 if ( i > - 1 ) this . hub [ event ] . splice ( i , 1 ) ;
13 if ( this . hub [ event ] . length === 0 ) delete this . hub [ event ] ;
14 }
15 } ) ;
16
17 const handler = data => console . log ( data ) ;
18 const hub = createEventHub ( ) ;
19 let increment = 0 ;
20
21 // Subscribe:
22 hub . on ( 'message' , handler ) ;
23 hub . on ( 'message' , ( ) => console . log ( 'Message event fired' ) ) ;
24 hub . on ( 'increment' , ( ) => increment ++ ) ;
25
26 // Publish
27 hub . emit ( 'message' , 'hello world' ) ;
28 hub . emit ( 'message' , { hello : 'world' } ) ;
29 hub . emit ( 'increment' ) ;
30
31 // Unsubscribe
32 hub . off ( 'message' , handler ) ;
14、补充一点关于函数length属性
1 4
5 function a ( a , b = 10 , c ) { } //a.lenght = 1
6 a ( 10 , '' , 30 ) // a = 10 b = '' c = 30
7 a ( 10 , null , 30 ) // a = 10 b = null c = 30
8 a ( 10 , undefiend , 30 ) // a =10 b = 10 c = 30
9 // 对于包含默认值的参数,只有传递 undefiend 时(或者不传)才是取默认值,其他情况均为传啥就是啥