JS 代码编程题

统计字符串中字母的个数 

const res = 'aaaabbbccccddfgh';

function getMoreTime(str) {
  const obj = {};
  for (let i = 0;i < str?.length;i++) {
    const zi = str.charAt(i);
    if (Object.keys(obj).indexOf(zi) > -1) {
      // 能找到就加1
      const num = obj[zi] + 1;
      obj[zi] = num;
    } else {
      // 不能找到就赋值为1开始
      obj[zi] = 1
    }
  };

  let max = 0;
  let mStr = '';
  const len = Object.keys(obj).length;
  const kArr = Object.keys(obj);
  for (let j = 0;j < len;j++) {
    const n = obj[kArr[j]]
    if (n > max) {
      max = n;
      mStr = kArr[j];
    }
  }
  return `出现次数最多的字母是:${mStr},总共出现了${max}次。`;
}
console.log(getMoreTime(res));

代码编程题

// 给所有的string对象添加去除首尾空白符的方法
String.prototype.myTrim = function() {
  const reg = /^\s+|\s+$/g;
  return this.replace(reg, '')
}
const str = '   sdkfhka  ';
console.log(str.myTrim());

代码输入题

// 非严格模式下
(function () {
  var a = b = 3;
})()

console.log("a defined " + (typeof a !=='undefined'));// false
console.log("b defined " + (typeof b !=='undefined'));// true

事实上,var a=b=3实际是以下声明的简写:
b = 3;
var a = b;

b因为没有使用var关键字,就变成了一个全局变量,在函数体内部,因为对a使用了var关键字,a就是一个局部变量,因此在函数体外,a就是一个undefined,b就是为3;
因此(如果你不使用严格模式),该代码段的输出如下。
a defined false
b defined true

// 严格模式下
'use strict';
(function () {
  var a = b = 3;
})()

console.log("a defined " + (typeof a !=='undefined'));// false
console.log("b defined " + (typeof b !=='undefined'));// true

在严格模式下,语句var a=b=3将生成 Reference error:b is not defined的运行时错误;

因为在严格模式下变量必须先生命再使用,所以会抛出错误。


代码编程题:

console.log(0.1 === 0.1);// true

console.log(0.1 + 0.2 === 0.3);// false

为什么不相等?

解释原因:因为JS存在计算精度不准确的问题。

JavaScirpt 使用 Number 类型来表示数字(整数或浮点数),遵循 IEEE 754 标准,通过 64 位来表示一个数字(1 + 11 + 52)

  • 1 符号位,0 表示正数,1 表示负数 s
  • 11 指数位(e)
  • 52 尾数,小数部分(即有效数字)

最大安全数字:Number.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1,转换成整数就是 16 位,所以0.1 === 0.1,是因为通过 toPrecision(16) 去有效位之后,两者是相等的。

在两数相加时,会先转换成二进制,0.1 和 0.2 转换成二进制的时候尾数会发生无限循环,然后进行对阶运算,JS 引擎对二进制进行截断,所以造成精度丢失。

所以总结:精度丢失可能出现在进制转换和对阶运算中。


代码编程题,实现数组扁平化:

// JS 实现数组扁平化
const arr = [1, 2, [2, 3, 5, [8, 10]], [78, 45]]

// 实现数组扁平化的方法一,用正则表达式
function flatArr1(array) {
  const reg = /\[|\]/g;
  const nArr = JSON.stringify(array).replace(reg, '').split(',');
  return nArr;
}

// 实现数组扁平化的方法二,用ES6提供的数组新方法也是可以的
function flatArr2(array) {
  return array.flat(Infinity)
}

// 实现数组扁平化的方法三,采用递归
const nArr = []
function flatArr3(array) {
  array.forEach((item) => {
    if (Array.isArray(item)) {
      flatArr3(item)
      return;
    }
    nArr.push(item)
  })
  return nArr
}


// console.log(flatArr1(arr));
// console.log(flatArr2(arr));
console.log(flatArr3(arr));

用JS实现一个单向链表:

class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class LinkedList {
  constructor() {
    this.head = null;
  }

  append(data) {
    const newNode = new Node(data);
    if (this.head === null) {
      this.head = newNode;
    } else {
      let current = this.head;
      while (current.next !== null) {
        current = current.next;
      }
      current.next = newNode;
    }
  }

  toString() {
    let current = this.head;
    let listString = '';
    while (current !== null) {
      listString += current.data + (current.next ? ' -> ' : '');
      current = current.next;
    }
    return listString;
  }
}

// 使用链表
const linkedList = new LinkedList();
linkedList.append(1);
linkedList.append(2);
linkedList.append(3);
console.log(linkedList.toString()); // 输出: 1 -> 2 -> 3

var b = 10;
(function b(){
  b = 20;
  console.log(b);
})();
// function,当同名的时候,函数声明的优先级高于变量声明的优先级

实现一个LazyMan的功能
class LazyManClass {
  constructor(name) {
    this.name = name;
    this.queue = [];
    console.log(`Hi I am ${name}`);
    setTimeout(() => {
      this.next();
    }, 0)
  }
  sleep (time) {
    const fn = () => {
      setTimeout(() => {
        console.log(`等待了 ${time} 秒...`);
        this.next();
      }, time * 1000)
    }
    this.queue.push(fn)
    return this;
  }
  eat (meal) {
    const fn = () => {
      console.log(`I am eating ${meal}`);
      this.next();
    }
    this.queue.push(fn)
    return this;

  }
  next () {
    const fn = this.queue.shift();
    fn && fn()
  }
}

function LazyMan(name) {
  return new LazyManClass(name);
}
function lunchLazyMan(name) {
  return new LazyManClass(name);
}
// LazyMan('Tony')
// LazyMan('Tony').sleep(10).eat('lunch');
lunchLazyMan('Tony').eat('lunch').sleep(2).eat('dinner');

模拟实现一个Promise.finally
// 模拟实现一个finally
Promise.prototype._finally = function(callback) {
  let P = this.constructor;
  // console.log('从下的代码可以知道不管成功还是失败,都走了callback');
  return this.then(
    value => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => {
      throw reason;
    }),
  )
}

new Promise((resolve) => {
  resolve()
}).then(() => {

}).catch(() => {

})._finally(() => {

})

手动实现一个Promise.allSettled

  const p1 = new Promise(resolve => {
    resolve(1111)
  })
  const p2 = new Promise(resolve => {
    resolve(2222)
  })
  const p3 = new Promise(resolve => {
    resolve(3333)
  })

  Promise.allSettled([p1, p2, p3]).then((res) => {
    console.log(res);
  })

  function AllSettled(promises) {
    return Promise.all(
      promises.map(promise => {
        return promise.then(
          value => ({ status: 'fulfilled', value }),
          reason => ({ status: 'rejected', reason }),
        )
      })
    )
  }
  AllSettled([p1, p2, p3]).then(res => {
    console.log('手动封装"', res);
  })


JS算法题「旋转数组」

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1, 2, 3, 4, 5, 6, 7] 和 k = 3 输出: [5, 6, 7, 1, 2, 3, 4] 解释: 向右旋转 1 步:
[7, 1, 2, 3, 4, 5, 6] 向右旋转 2 步: [6, 7, 1, 2, 3, 4, 5] 向右旋转 3 步: [5, 6, 7, 1, 2, 3, 4]
示例 2:
输入: [-1, -100, 3, 99] 和 k = 2 输出: [3, 99, -1, -100] 解释: 向右旋转 1 步: [99, -1, -100, 3] 向右旋转 2 步: [3, 99, -1, -100]

// 用数组的splice方法
function calcArr(data, k) {
  const len = data.length - k;
  const arr1 = data.splice(0, len)
  return [...data, ...arr1]
}

const arr1 = [-1, -100, 3, 99];

console.log(calcArr(arr1, 2));

打印出1 - 10000之间的对称数
function getAll() {
  const arr = [];
  for (let i = 10; i <= 10000; i++) {
    const str = String(i);
    const rStr = str.split('').reverse().join('');
    if (str === rStr) {
      arr.push(i)
    };
  }
  return arr;
}

console.log(getAll());

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12] 输出: [1,3,12,0,0]
复制代码说明: 必须在原数组上操作,不能拷贝额外的数组。 尽量减少操作次数

function moveZero(data) {
  let j = 0
  for (let i = 0; i < (data.length - j); i++) {
    if (data[i] === 0) {
      data.push(0);
      data.splice(i, 1)
      i--;
      j++
    };
  }
  return data;
}

const arr = [0,1,0,3,12];
console.log(moveZero(arr));// [1, 3, 12, 0, 0]

请实现一个函数add,满足以下功能:

console.log(add(1)(2)(3));// 6
console.log(add(1,2,3));// 6
console.log(add(1)(2,3));// 6
console.log(add(1,2)(3));// 6

很明显这里要用到函数柯里化,如下实现代码:

const currying = fn =>
  judge = (...args) =>
    args.length >= fn.length
      ? fn(...args)
      : (...arg) => judge(...args, ...arg);


const add = currying(function(a, b, c) {
  return a + b + c;
});
console.log(add(1)(2)(3));// 6
console.log(add(1,2,3));// 6
console.log(add(1)(2,3));// 6
console.log(add(1,2)(3));// 6

 找出数组中和为目标值的两个数对应的下标。

示例:
给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]

function getIndex(data, tgt) {
  let arr = [];
  const len = data.length;
  flag:for (let i = 0; i < len-1; i++) {
    for (let j = 1; j < len; j++) {
      if (data[i] + data[j] === tgt) {
        arr = [i, j];
        break flag;
      }
    }
  }
  return arr;
}

console.log(getIndex(nums, 18));//[1, 2]

现convert方法,把原始list转换成树形结构
let list = [
  {id:1,name:'部门 A',parentId:0},
  {id:2,name:'部门 B',parentId:0},
  {id:3,name:'部门 C',parentId:1},
  {id:4,name:'部门 D',parentId:1},
  {id:5,name:'部门 E',parentId:2},
  {id:6,name:'部门 F',parentId:3},
  {id:7,name:'部门 G',parentId:2},
  {id:8,name:'部门 H',parentId:4}
];

function convert(list) {
  const res = []
  const map = list.reduce((res, v) => (res[v.id] = v, res), {})
  for (const item of list) {
    if (item.parentId === 0) {
      res.push(item)
      continue
    }
    if (item.parentId in map) {
      const parent = map[item.parentId]
      parent.children = parent.children || []
      parent.children.push(item)
    }
  }
  return res
}
console.log(convert(list));

简述设计并实现Promise.race()

Promise._race = promises => new Promise((resolve, reject) => {
  promises.forEach(promise => {
    promise.then(resolve, reject)
  })
})

const p1 = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(1)
    }, 20)
  })
}
const p2 = () => {
  return new Promise((resolve) => {
    resolve(2)
  })
}

Promise._race([p1(), p2()]).then(res => {
  console.log(res);// 2
})

查找一个树形结构对应的数组中,链条中所有的父级id

实现思路:

1. 先将数组扁平化处理,通过 parentId 建立联系
2. 然后查找到key对应的项A,
3. 在项A中的 parentId 去查找,直到查找到 parentId 为null,
4. 返回查找到的所有parentId
const list =  [
  {
    id: "1",
    name: "test1",
    children: [
      {
        id: "99",
        name: "test11",
        children: [
          {
            id: "111",
            name: "test111"
          },
          {
            id: "112",
            name: "test112"
          }
        ]
      },
      {
        id: "12",
        name: "test12",
        children: [
          {
            id: "121",
            name: "test121"
          },
          {
            id: "122",
            name:
              "test122"
          }
        ]
      }
    ]
  }
];

const find = (value) => {
  const arr = []

  const all = (data, pId) => {
    data.forEach((item) => {
      arr.push({
        id: item.id,
        name: item.name,
        parentId: pId,
      })
      if (item?.children?.length) {
        all(item.children, item.id)
      }
    })
  }
  all(list, null)
  // arr就是扁平化之后的数组,这里要注意一下了。
  const res = []
  const loop = (sKey) =>{
    const obj = arr.find(item => item.id === sKey);
    obj && res.unshift(obj.id)
    if (obj?.parentId) {
      loop(obj.parentId)
    }
  }
  loop(value)
  console.log(res);
  return res;
}
find('111'); // ['1', '99', '111']
find('1562'); // []

实现一个call函数

Function.prototype._call = function (thisArg, ...args) {
  if (thisArg === null || thisArg === undefined) {
    thisArg = window
  } else {
    thisArg = Object(thisArg)
  }

  thisArg.fn = this;
  const result = thisArg.fn(...args)
  delete thisArg.fn;
  return result;
}


function fn() {
  console.log(this.a);
}
const obj1 = {
  a: 1
}
const obj2 = {
  a: 2
}
fn._call(obj2)

以下代码的执行结果:

function test(a){
a=a+10;
}
var a=10;
test(a);
console.log(a);//10

 注意一下,最后的结果是10,在函数test内部修改a的值为20,但是因为是在函数外部访问的a,所以,随后的结果是10。


最终x为多少?

var x=0;
switch(++x) {
  case 0: ++x;
  case 1: ++x;
  case 2: ++x;
}
console.log(x);//3

因为没有break,所以会一直执行;因此是3。


以下代码运行结果:

(function() {
  var a = b = 5;
})();
console.log(b);// 5
console.log(a);// Uncaught ReferenceError: a is not defined

(function() {
  var x=foo();
  var foo=function foo() {
    return "foobar"
  };
  return x;
})();

以上代码会抛出错误,Uncaught TypeError: foo is not a function.

原因:

  • 预处理时,变量声明和函数声明的优先级,先变量后函数。当变量名和函数名一致时,函数会覆盖变量。
  • 只提升了声明,没有提升赋值。

用hook实现一个倒计时

export default function useCountDown(num) {
  const [count, setCount] = useState(num)
  let refInterval = useRef()

  const start = useCallback(() => {
    refInterval.current = setInterval(() => {
      // 注意这里是关键,一定要是一个函数
      setCount((count) => count - 1)
    }, 1000)
  }, [count])

  const stop = () => {
    refInterval.current && clearInterval(refInterval.current)
  }

  return { start, count, stop }
}


// 具体的引用
export default function Lili() {
  const { start, count, stop } = useCountDown(60);

  return (
    <>
      <div>倒计时: {count} </div>
      <button onClick={start}>start </button>
      <button onClick={stop}>stop </button>
    </>
  )
}

模拟实现Promise.all

    function myPromiseAll(arr: any[]) {
      let resultArr: any = [];
      return new Promise((resolve) => {
        if (arr.length === 0) {
          resolve(arr);
        }
        let count = 0;
        for (let i = 0; i < arr.length; i += 1) {
          Promise.resolve(arr[i])
            .then((value) => {
              resultArr[i] = value;
              // 手动计时器,因为传进来的setTimeout是异步,还没执行完毕,resultArr.length已经是3了。
              if (++count === arr.length) {
                resolve(resultArr);
              }
            })
            .catch((err) => console.log('promiseAll 出错啦', err));
        }
      });
    }
    const p1 = new Promise((resolve) => resolve(1));
    const p2 = new Promise((resolve) => {
      setTimeout(() => {
        resolve(2);
      }, 2000);
    });
    const p3 = new Promise((resolve, reject) => resolve(3));

    myPromiseAll([p1, p2, p3]).then((res) => {
      console.log(res);//[1, 2, 3]
    });

写出代码的执行结果:

const promise = new Promise((resolve, reject) => {
  console.log(1);
  setTimeout(() => {
    console.log("timerStart");
    resolve("success");
    console.log("timerEnd");
  }, 0);
  console.log(2);
});

promise.then((res) => {
  console.log(res);
});

console.log(4);

以上代码的执行结果为:

// 1
// 2
// 4
// timerStart
// timerEnd
// success

可能会纠结为啥success会在timerEnd之后打印,为啥不是在timerStart之前打印呢?

因为同步任务执行完毕以后,去执行promise对应的微任务的时候,promise的状态还是pending状态,所以无法执行promise.then里面的代码,所以先去执行了setTimeout里面的代码,执行完之后,promise的状态变成了fulfilled,这个时候就可以打印success了。


以下代码的执行结果:

const timer1 = setTimeout(() => {
  console.log('timer1');

  const promise1 = Promise.resolve().then(() => {
    console.log('promise1')
  })
}, 0)

const timer2 = setTimeout(() => {
  console.log('timer2')
}, 0)

以上代码的执行结果为:

// timer1
// promise1
// timer2

因为,正确的执行原则是:

  1. 首先执行所有微任务

  2. 执行宏任务

  3. 再次执行所有(新添加的)微任务

  4. 执行下一个宏任务

  5. 绕圈穿过


以下代码的执行结果是:

console.log('start');

const promise1 = Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});

const timer1 = setTimeout(() => {
  console.log('timer1')
  const promise2 = Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)

console.log('end');

控制台打印顺序是:

// start
// end
// promise1
// timer1
// promise2
// timer2

答对上面的顺序,需要记住:

记住我们之前学到的:

  1. 同步代码

  2. 所有微任务

  3. 第一个宏任务

  4. 所有新添加的微任务

  5. 下一个宏任务

队列和栈的执行顺序不同,队列是先进先出,栈是先进后出,这也要注意一下。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值