js基础必会,面试灵魂拷问

1,395 阅读8分钟

一、函数的arguments是不是数组,如何转化为数组?

arguments本身并不能调用数组的方法,它是另外一种对象类型,只不过属性从0开始排,一次是0,1,2...最后还有callee和length属性。我们把这样的对象成为类数组。

常见的类数组还有:

  • getElementsByClassName/getElementsByTagName
  • querySelector 

我们需要将他们转换成数组,从而能够使用数组的方法,有哪些方法可以转换呢?

1、Array.from

2、[...arguments]

3、Array.prototype.slice.call(arguments)

4、最原始的再创建一个数组,遍历类数组属性把每个属性值放在里面

 二、数组方法有哪些?遍历方法有何区别?

1、遍历方法

  • 只有for系列中才有continue、break    
  • for
    常规的for循环,continue、break、return有效
  • for...in
    遍历的是数组的key,是string类型。数组的私有属性也会遍历,continue、break、return有效。也可以在对象中,字符串中使用。数组的私有属性也会遍历。

    let arr= ['a','b','c','d']
    arr[4] = 'f'
    for(let key in arr){
      console.log(key) // 0,1,2,3,4
    }

  • for...of //es6新增方法
    es6新增的方法,弥补了forEach和for...in的弊端,有continue、break、return,不会遍历数组的私有属性。遍历的是iterable(数组、map、set)的value,不能在对象、字符串中使用

    let arr= ['a','b','c','d']
    arr[4] = 'f'
    for(let val of arr){
      console.log(val) // a,b,c,d
    }

  • forEach
    没有return
  • map
    映射
  • filter
    过滤
  • every
    所有的都返回true,则返回true,否则返回false
  • some
    某一项返回true,则返回true,否则返回false
  • reduce
    聚合,reduce函数接收两个参数,callback和initiaValue
    reduce((accumulator, currentValue, currentIndex(可选), array(可选))=>{},initalValue(可选))。重点accumulator为initialValue或者每一次回调的返回值

    //统计字符串每个字母出现的次数
    let str = '23wer43ertert'
    //常规做法
    let ret = {}
    Array.from(str).forEach(item=>{
      ret[item]?ret[item]++:ret[item]=1
    )
    return ret
    
    // 使用reduce
    Array.from(str).reduce((accumulator, current) => { 
     accumulator[current] ? accumulator[current]++ : accumulator[current] = 1 
     return accumulator}, {})

2、栈和队列操作方法

  • push
    尾部追加元素
  • pop
    尾部移除一个元素
  • shift
    头部移除一个元素
  • unshift
    头部添加元素

3、es6新增方法

  • find
  • findIndex
  • includes
  • for...of
  • flat
  • keys
    es6提供三个新的方法遍历数组,keys、values、entries,他们都返回一个遍历器对象Array.Iterator,可以用for of循环进行遍历,唯一的区别是keys()是对键明的遍历,values()是对健值得遍历,entries是对键值对的遍历

    let arr = [1,2,3]
    for(let key of arr.keys()){
      console.log(key) // 0,1,2
    }

  • values

    let arr = [1,2,3]
    for(let val of arr.values()){
      console.log(val) // 1,2,3
    }

  • entries

    let arr = [1,2,3]
    for(let [index, ele] of arr.entries()){
      console.log(index, ele) // 0,1 1,2 2,3
    }

  • set
    set和map是es6新增的数据结构,可以把set和map传递给Array.from(),生成一个新的数组。set
  • map

4、其他常用方法

  • slice
    slice(start, end),end可选,包前不包后,如果只有一个参数,则截取该下标到最后,与substring类似

    let arr = [1,2,3]
    let res = arr.slice(1,2)
    console.log(res) //[2]
    let res2 = arr.slice(1)
    console.log(res2) // [2,3]

  • splice
    splice(index, num, val...),index索引,num删除的数量,val(可选),插入的值,原数组会改变。

    let arr = [1,2,3]
    arr.splice(0,1)
    console.log(arr) // [2,3]
    arr.splice(1,1,4,5,6) // [2,4,5,6]
    console.log(arr)

  • sort
  • concat
  • join
  • indexOf
  • reverse
  • isArray
  • toString

5、判断数组是否包含某个值

  • indexOf
  • includes
  • find
  • findIndex

6、数组扁平化

在前端开发中,偶尔会出现数据结构重叠数组,我们需要将多层级数组转化为一级数组,使其内容合并并且展开。

let arr = [1,[2,3,[4],5,[6]],7] // => [1,2,3,4,5,6,7]
  • 调用es6中的flat方法

    let ret = arr.flat(Infinity) // 参数为扁平化深度

  • repace+split

    let str = JSON.stringify(arr)
    return str.replace(/\[|\]/g, '').split(',')

  • replace+parse

    let str = JSON.stringify(arr)
    str = `[${str.replace(/\[|\]/g,'')}]`
    return JSON.parse(str)

  • 普通递归

    let ret = []
    let flat = function(arr){
      for(let i=0;i<arr.length;i++){
        if(Array.isArray(arr[i])){
          flat(arr[i])
        }else{
          ret.push(arr[i])
        }
      }
      return flat
    }

  • 使用reduce函数迭代

    function flat(arr){
     return arr.reduce((accumulator, current)=>{
        return Array.isArray(current)? accumulator.concat(flat(current)): 
          accumulator.concat(current)
      }, [])
    }

  • 展开运算符

    while(arr.some(item=>Array.isArray(item))){
      arr = [].concat(...arr)
    }
    return arr

三、js数组中的高阶函数

1、什么是高阶函数

一个函数,如果接收另一个函数作为参数,或者返回值为一个函数,这种函数我们称之为高阶函数。

2、数组中的高阶函数

  • map
  • reduce
  • filter
  • sort

四、实现数组的map方法

Array.prototype.myMap = function (cb, scope) {  // 判断类型  if (!Array.isArray(this)) {    throw new TypeError("不是数组。。。。")  }  if (typeof cb !== 'function') {    throw new TypeError(`${cb} is not a function`)  }  let T = Object(this) // arr  let len = T.length, ret = []  for (let i = 0; i < len; i++) {    ret.push(cb.call(scope, T[i], i, T))  }  return ret}

五、实现数组的reduce方法

Array.prototype.myReduce = function(fn, initiaValue){
  let arr = this, k=0,accomulator
  if(!Array.isArray(arr)){
    throw new Error('is no Array')
  }
  if(arr.length === 0){
    throw new Error('array is empty')
  }
  if(!initiaValue){
    accommulator = arr[0]
     k++
  }

  for(;k<arr.length;k++){
    accomulator = fn(acommulator, arr[k], k, arr)
  }
  return accomulator
}

六、实现数组的push、pop方法

Array.prototype.myPush = function(v){  console.log(this)  let T = Object(this)  if(!Array.isArray(T)){    throw new TypeError('不是数组。。。')  }  let len = T.length  T[len] = v  return v}

Array.prototype.myPop = function () {  let T = Object(this), ret  if (!Array.isArray(T)) {    throw new TypeError('不是数组。。。')  }    let len = T.length;  if (len === 0) {    throw new TypeError('empty array')  }  ret = T[len - 1]  delete T[len-1]  T.length = len-1  return ret}

七、实现数组的filter方法

Array.prototype.myFilter = function (cb, scope) {  let T = Object(this)  if (!Array.isArray(T)) {    throw new TypeError('不是数组。。。')  }  if (typeof cb !== 'function') {    throw new TypeError(`${cb} is not a function`)  }  let len = T.length, ret = []  if (len === 0) {    throw new TypeError('array is empty...')  }  for (let i = 0; i < len; i++) {    if(cb.call(scope, T[i], i, T )){      ret.push(T[i])    }  }  return ret} 

八、实现数组的splice方法


九、实现数组的sort方法


十、谈谈你对this的理解

this是当前函数上下文环境,是动态确定的,在函数运行时才能确定this所指对象

  • 显示绑定
    call、apply、bind
  • 隐式绑定
    1、全局上下文:全局上下文默认this指向window,严格模式下指向undefined
    2、直调用函数:指向window

    let obj = {
      fn: function(){
        console.log(this)
      }
    }
    
    let f = obj.fn
    f() //window
    

       3、对象.方法调用:上面obj.fn(),this指向obj对象

       4、dom事件绑定

           onclick或addEventListener中this默认指向绑定事件的元素

       5、new+构造函数

          this指向构造函数实例对象

       6、箭头函数

          箭头函数没有this,里面的this指向当前最近的非箭头函数的this

let obj = {
  fn: function(){
    let do =()=>{
      console.log(this) 
    }
    do()
  }
}
obj.fn() //obj

十一、模拟实现call、apply、bind

  • call

    Function.prototype.myCall = function(context, ...rest){
      context.fn = this
      context.fn(rest)
      delete context.fn
    }
    
    let obj = {
     name: 'ky'
    }
    function fn(...args){
      console.log(this.name, args[0])
    }
    fn.myCall(obj, 1,2,3)
    

  • apply

    Function.prototype.myApply = function(context, ...rest){
      context.fn = this
      context.fn(rest)
      delete context.fn
    }
    
    let obj = {
      name: 'wky'
    }
    function fn(){
      console.log(this.name, arguments)
    }
    fn.myApply (obj, [1,2,3])

  • bind

    Function.prototype.myBind = function(context, ...rest){
      if(typeof this !== 'function'){
        throw new Error('no a function')
      }
      context.fn = this
      return function (){
        context.fn(...rest)
         delete context.fn
      }
    }
    
    let obj = {
      name: 'wky'
    }
    function fn(){
      console.log(this.name, arguments)
    }
    fn.myBind(obj, [1,2,3])()

十二、模拟实现new

function Person(name, age){
  this.name = name
  this.age = age
}
Person.prototype.height = 100

function myNew(cons, ...rest){
  let obj = Object.create(cons.prototype) //把构造函数原型作为新对象的原型,实现原型链上的继承
  obj.fn = cons
  obj.fn(...rest)
  delete obj.fn
  return obj
}
let p = myNew(Person, 'wky', 18)
console.log(p)


十三、js中的浅拷贝有哪些

  • 什么是浅拷贝

    let arr = [1,2,3]
    let newArr = arr
    直接赋值这不涉及任何的拷贝,他们引用的是同一块内存空间。浅拷贝只是拷贝第一层对象,如果对象嵌套
    浅拷贝无能为力
  • 浅拷贝的方式
    1、slice

    let arr = [1,2,3,{a: 4}]
    let newArr = arr.slice()
    newArr[0] = 4
    newArr[3].a = 5
    console.log(arr) // [1,2,3, {a:5}]

    2、Object.assign

    let obj = {a: 1}
    let newArr = Object.assign(obj,{a:2})

    3、...

    let obj = {a:1,b:2,c:{d:3}}
    let obj2 = {...obj}

    4、concat数组

    let arr = [1,2,3,[1,2]]
    let arr2 = arr.concat([])

    5、手动实现

    function shallowCopy(target){
      let ret
      if(typeof target === 'object' && target !== null){
        ret = Array.isArray(target)?[]:{}
        for(let key in target){
          ret[key] = target[key]
        }
      }else{
        ret = target
      }
      return ret
    }

十四、手写一个深拷贝 

  • 简易版及问题JSON.parse(JSON.stringify())
    估计这个api能覆盖大多数的应用场景,但是对于某些严格的场景来说,这个api是有巨大坑的,问题如下
    1、无法解决循环引用问题,会出现系统栈溢出,因为无限递归

    let obj = {a: 1}
    obj.target = obj
    2、无法复制函数
    3、无法拷贝一些特殊对象,如Map,Set
  • 递归实现深拷贝,可以复制函数,解决循环引用,解决Map、Set特殊对象
    使用map保存已经拷贝过得对象,如果已经拷贝过,直接返回
    Map,Set数据结构特殊处理
  •     const getType = target => Object.prototype.toString.call(target)    function deepClone(target, map = new WeakMap()) {      if (typeof target === 'object' && target !== null) {        if (map.get(target)) return target        map.set(target, true)        let type = getType(target)        let ret = new target.constructor()        switch (type) {          case '[object Map]':            target.forEach((item, key) => {              ret.set(key, deepClone(item, map))            })            break;          case '[object Set]':            target.forEach(item => {              ret.add(deepClone(item, map))            })            break;          default:            for (let key in target) {              ret[key] = deepClone(target[key], map)            }        }        return ret      } else {        return target      }    }

十五、谈谈对原型链继承的理解,手写一个继承

  • 构造函数、实例对象、原型、原型链
    画图展示
  • 原型链继承+构造函数继承=组合式继承
        function Foo() {      this.name = 'foo'    }    Foo.prototype.fn = function () {      console.log('foo 方法');    }    function Coo() {      // 继承父类构造函数里的属性和方法      Foo.call(this)      this.type = "coo"    }    Coo.prototype.cooFn = function () {      console.log('coo fn');    }    Coo.prototype = new Foo() //原型链上的继承,把父类实例对象挂到子类原型上

  • es6继承

     class Foo {      constructor() {        this.name = 'Foo'        this.fn = function () { }      }    }    class Coo extends Foo {      constructor(props) {        super(props)        this.type = "Coo"      }    }

  • instanceof的理解,手写一个instanceof
    原理是在原型链上找,如果找到原型链上有该构造函数的原型,返回true,否则返回false

    function myInstanceof(left, right) {
      while (true) {
     if (left === null) {
          return false
        }
        if (left.constructor === right) {
          return true
        }
       
        left = left.__proto__
      }
    }

  • 如何判断一个数据类型
    1、基本数据类型用typeof
    2、引用数据类型
         constructor
         Object.prototype.toString.call(target)
    3、判断一个数组
         arr.constructor === Array
         Object.prototype.toString.call(arr) // [object Array]
         Array.isArray(arr)
         arr instanceof Array

十六、深入理解js异步

  • 什么是异步
    1、为了避免dom渲染冲突,js是单线程的,也就是同一时间只能做一件事。异步是js单线程的解决方案。让同步代码先执行,等一段时间再执行异步的内容
    2、常见的异步操作
        微任务:promise、process.nextTick、
        宏任务:setTimeout、setInterval、setImediate、
  • 异步和event-loop

    console.log('1');
    
    setTimeout(function() {
        console.log('2');
        process.nextTick(function() {
            console.log('3');
        })
        new Promise(function(resolve) {
            console.log('4');
            resolve();
        }).then(function() {
            console.log('5')
        })
    })
    process.nextTick(function() {
        console.log('6');
    })
    new Promise(function(resolve) {
        console.log('7');
        resolve();
    }).then(function() {
        console.log('8')
    })
    
    setTimeout(function() {
        console.log('9');
        process.nextTick(function() {
            console.log('10');
        })
        new Promise(function(resolve) {
            console.log('11');
            resolve();
        }).then(function() {
            console.log('12')
        })
    })
    // 1,7,6,8,2,4,3,5,9,11,10,12

    1、事件轮询是是js异步的具体实现。同步代码放在主线程中,异步的进入event-table并注册函数,
    2、当指定的事件完成时,event-table会将这个函数移入event-queue
    3、主线程内的任务执行完毕,会去event-queue读取对应的函数,进入主线程执行
    4、先执行微任务,再执行宏任务
    4、上述过程不断重复,也就是常说的event-loop
  • jq的解决方案
  • promise
    1、了解 Promise 吗?
    2、Promise 解决的痛点是什么?
    3、Promise 解决的痛点还有其他方法可以解决吗?如果有,请列举。
    4、Promise 如何使用?
    5、Promise 常用的方法有哪些?它们的作用是什么?
    6、Promise 在事件循环中的执行过程是怎样的?
    7、Promise 的业界实现都有哪些?
    8、能不能手写一个 Promise 的 polyfill。
  • generator 
  • async-await

十七、实现一个防抖函数

let st
function deounce(fn, wait){
  if(st){
   clearTimeout(st)
  }
  st = setTimeout(()=>{
   fn()
  }, wait)
}

十八、实现一个节流函数

let st
function throttle(fn, wait){
  if(!st){
    st = setTimeout(()=>{
      st = null
      fn()
    }, wait)
  }
}

十九、实现一个冒泡排序

function bubble(arr){
  let len = arr.length
  for(let i=0;i<len;i++){
    for(let j=0;j<len;j++){
      if(arr[j]>arr[j+1]){
        let tem
        tem = arr[j]
        arr[j] = arr[j+1]
        arr[j+1] = tem
      }
    }
  }
  return arr
}

二十、实现一个快速排序

function quickSort(arr){
  if(arr.length <= 1){
    return arr
  }
  let left = [], middle = [arr[0]], right = [], base = arr[0]
  for(let i=0;i<arr.length;i++){
    if(arr[i]<base){
      left.push(arr[i])
    }else if(arr[i] === base){
      middle.push(arr[i])
    }else{
      right.push(arr[i])
    }
  }

  return quickSort(left).concat(middle, quickSort(right))
}

二十一、BFC

1、盒模型

盒模型包括标准盒模型和IE盒模型
内容:content、padding、border、margin
区别:计算width和height方式不同,标准盒模型:content,IE盒模型content+padding+border
设置:标准盒模型:box-sizing:content-box,IE盒模型:box-sizing:border-box

2、BFC

概念:块级格式化上下文,是一块渲染区域,满足下面声明的元素会生成BFC

  • float不为none
  • overflow不为visible
  • display为inline-block
  • position为absolute或fixed

应用:

  • 解决边距重叠问题:两个兄弟元素,margin边距会重叠,取较大者。

    <style>    #A {        width: 100px;        height: 100px;        background: red;        margin-bottom: 100px;    }    #B {        width: 100px;        height: 100px;        background: blue;        margin-top: 200px;    }</style><body>    <div id="A">A</div>
        <div style="overflow: hidden">
           <div id="B">B</div>
        </div></body>

    解决方法:将两个元素放到不同BFC环境下就好了。如A在body环境下,margin-bottom:100,B在box环境下,margin-top:200,给box加上BFC
  • 清除浮动:外面的outer并没有被内容撑开,原因是浮动脱离了文档流,与普通元素所处的流层不一样,不会被撑开
    解决方法:在outer加上bfc

     <style>
           *{
               margin: 0;
               padding: 0;
           }
           .outer{
               border: 2px solid black; 
               margin:100px; 
               overflow: hidden;
           }
           .inner{
               width: 100px;
               height: 100px;
               float: left;
           }
           .red{
            background: red;
           }
           .green{
            background: green;
           }
           .blue{
            background: blue;
           }
        </style>
    </head>
    <body>
        <div class="outer">
            <div class="inner red"></div>
            <div class="inner green"></div>
            <div class="inner blue"></div>
        </div>
    </body>

二十二、三栏布局

  • 浮动
  • 绝对定位
  • flex布局

二十三、flex布局

二十四、事件委托

本质:事件冒泡,节约很多内存开销

二十五、正则匹配手机号

/^1(3|4|5|7|8)\d{9}$/

二十六、webpack配置

  • webpack定义
    webpack是一个模块打包工具,用来管理项目中的模块依赖,并编译输出模块所需的静态文件。它可以很好地管理、打包开发中所用到的HTML、CSS、JS和静态文件等,让开发更高效
  • webpack基本功能和工作原理
    1、代码转换:TypeScript编译成js、scss编译成css等
    2、文件优化:压缩js、css、HTML代码,压缩合并图片等
    3、代码分割:提取多个页面的公共代码,提取首屏不需要执行部分的代码,让其异步加载
    4、模块合并:某个模块引用其他的模块和文件,需要构建功能,把模块合并成一个文件
    5、热更新:监听本地原代码变化,自动构建,更新内容
  • webpack配置结构
    1、mode: 'development' // production
    2、entry:打包文件入口

    entry: {
      page1: './page1.js',
      page2: './page2.js'
    }

    3、output:指定打包后资源位置

    output: {
      path: path.resolve(__dirname__, './dist'),
      filename: '[name].[hash].js'
    }

    4、module: {rules: []}: 配置各种loader

    module: {
      rules: [{
        test: /\.(jpg|png|jpeg|svg)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: './imgs',
            limit: 2048
          }
        }
      }]
    }

    5、plugins: []

    plugins: [new HtmlWepackPlugin({template: './index.html'})]

    6、devServer: {port: ,hot: true, proxy: '/api':{target: ''}}
    7、optimization:代码分割等配置
  • 使用过哪些loader
    1、file-loader: 打包文件
    2、url-loader: 打包文件,与file-loader不同在于

    {
      test: /\.(png|jpg|jpeg|svg)$/,
      use: {
        loader: 'url-loader',
        options: {
          limit: 1000,  //小于1000的文件打包出base64格式写入js,减少http请求
          outputPath: './imgs'
        }
      }
    }

    3、css-loader
    4、style-loader: 打包代码加入style标签中
    5、scss-loader
    6、postcss-loader:加上厂商前缀
    7、babel-loader:处理es6/7,jsx,typescript等转译
  • 使用过哪些plugin
    1、html-webpack-plugin:指定html模板

    plugins: [
      new HtmlWebpackPlugin({template: './index.html'})
    ]

    2、clean-webpack-plugin:打包时先清理源目录文件
    3、mini-css-extract-plugin:
    4、webpack.HotModuleReplacePlugin:热更新,使得代码修改后不用刷新浏览器就自动更新
  • 什么是tree-shaking?
    tree-shaking,摇树,是指在打包过程中,把没用到的代码除去,提高代码利用率,提高性能
  • 区分环境合并配置文件:webpack-merge
  • 代码分割:

    optimization: {
      splitChunks: {
        async: 'all'
      }
    }


  • webpack构建过程
    1、从entry里面配置的入口文件递归解析所有依赖的module
    2、根据配置的loader去找相应的转换规则
    3、以entry为单位,一个entry和其所有依赖的module打包成一个chunk
    4、把chunk转换成文件输出
    5、在整个过程中,webpack会在恰当时机执行plugin里面定义的逻辑
  • 手写一个loader
    loader就是一个函数,接收原代码source,然后处理原代码,最后return source即可。不能用箭头函数,因为this由webpack填充,可以访问一些方法和属性
  • 手写一个plugin
    1、定义一个js函数
    2、在函数原型上挂一个apply方法
    3、指定一个compile钩子
    4、处理完执行回调函数

二十七、面试题

Array.prototype.myMap = function (fn) { let arr = this if (!Array.isArray(arr)) { return 'no Array' } let ret = [] this.forEach((item, index) => { ret.push(fn(item, index, arr)) }) return ret}Array.prototype.myFilter = function (fn) { let arr = this if (Object.prototype.toString.call(arr) !== '[object Array]') { return 'no array' } let ret = [] arr.forEach((item, index) => { if (fn(item, index, arr)) { ret.push(item) } }) return ret}Array.prototype.myReduce = function (fn, inivialValue) { let arr = this, k = 0, accomulator if (!Array.isArray(arr)) { throw new Error('is no Array') } if (arr.length === 0) { throw new Error('arr is empty') } if (typeof fn !== 'function') { throw new Error('is not function') } if (!inivialValue) { k++ inivialValue = arr[0] } accomulator = inivialValue for (; k < arr.length; k++) { accomulator = fn(accomulator, arr[k], k, arr) } return accomulator}Function.prototype.myCall = function (context, ...args) { if (typeof this !== 'function') { throw new Error('is no a function') } context.fn = this context.fn(...args) delete context.fn}Function.prototype.myApply = function (context, args) { if (typeof this !== 'function') { throw new Error('is no function') } context.fn = this context.fn(...args) delete context.fn}Function.prototype.myBind = function (context, ...args) { if (typeof this !== 'function') { throw new Error('is no function') } context.fn = this return function () { context.fn(...args) delete context.fn }}function myNew(cons, ...args) { let obj = Object.create(cons.prototype) obj.fn = cons obj.fn(...args) delete obj.fn return obj}function myInstanceof(left, cons) { while (true) { if (left === null) { return false } if (left.__proto__ === cons.prototype) { return true } left = left.__proto__ }}function boubleSort(arr) { let len = arr.length for (let i = 0; i < len; i++) { for (let j = 0; j < len; j++) { if (arr[j] > arr[j + 1]) { let tem; tem = arr[j] arr[j] = arr[j + 1] arr[j + 1] = tem } } } return arr}function quickSort(arr) {    if (arr.length <= 1) return arr let left = [], right = [], middle = [arr[0]], base = arr[0] for (let i = 1; i < arr.length; i++) { if (arr[i] <= base) { left.push(arr[i]) } else if (arr[i] === base) { middle.push(arr[i]) } else { right.push(arr[i]) } } return quickSort(left).concat(middle, quickSort(right))}let arr = [45, 32, 2, 76, 34, 45, 87, 9, 2, 1, 32, 2]console.log(quickSort(arr))console.log((myInstanceof({}, Array)));function Person(name, age) { this.name = name this.age = age}function toCamal(str) { // find_index_sum => findIndexSum let arr = str.split('_') let res = arr.map((item, i) => { // for (let i = 0; i < arr.length; i++) { if (i !== 0) { return item[0].toUpperCase() + item.substring(1) } else { return item } // } }) return res.join('')}console.log(toCamal('find_index_sum'))let p = myNew(Person, 'ky', 18)// console.log(p);// let arr = [1, 2, 3]// let filterRes = arr.myFilter((item, index, arr) => {// console.log(item, index, arr);// return item > 1// })// let res = arr.myMap((item, i, arr) => {// return `${item}-${i}-${arr.toString()}`// })// let reduceRes = arr.myReduce((accomulator, current, index, arr) => {// console.log(accomulator, current, index);// return accomulator + current// }, 1)// console.log(reduceRes);// let obj = {// name: 'obj'// }// function fn() {// console.log(this.name, arguments);// }// fn.bind(obj, [1, 2, 3])()/** * 123456789=>123,456,789 9-6 =3, * @param {*} num 9-1-8=0 9-1-7=1 9-1-6=2 * 1234567 7 1,234,567 * 7-i=7 * 7-i=6 * 5 * 4 */function formateMoney(num) { num += '' let ret = '' for (let i = 0; i < num.length; i++) { if ((num.length - i) % 3 === 1) { ret += `${num[i]},` } else { ret += num[i] } } // ret = ret.split('').reverse().join('') return ret.endsWith(',') ? ret.substr(0, ret.length - 1) : ret}// console.log(formateMoney(12345678))/**给定一个升序整形数组[0, 1, 2, 4, 5, 7, 13, 15, 16,],找出其中连续出现的数字区间为如下:["0->2", "4->5", "7", "13", "15->16"] * * @param {*} arr */function summaryRanges(arr) { // 补全代码 let ret = [] for (let i = 0; i < arr.length; i++) { if (arr[i - 1] + 1 !== arr[i] && arr[i] + 1 === arr[i + 1]) { ret.push(`${arr[i]}->`) } else if (arr[i] === arr[i - 1] + 1 && arr[i] + 1 !== arr[i + 1]) { ret = ret.join(',').replace(/->$/, `->${arr[i]}`).split(',') } else if (arr[i] === arr[i - 1] + 1 && arr[i] + 1 === arr[i + 1]) { continue } else { ret.push(arr[i]) } } console.log(ret);}summaryRanges([0, 1, 2, 4, 5, 7, 13, 15, 16, 17, 18, 19, 20, 21, 4, 5, 6, 7, 8, 4, 2, 3, 1, 2, 3, 4, 5, 6, 7])