牛客网前端技能大挑战详解

三年前刷牛客里面的前端题,最近翻出来继续刷,发现了不少有趣的知识点,而且发现果然,两年时间还是有进步的~记录一下解题过程和答案~

FED1 修改this指向

题目:封装函数 f,使 f 的 this 指向指定的对象

// 改变this指向常用的有三个函数,运行之后函数a的this指向obj,prm是函数入参
// a.apply(obj, [prm1,prm2])
// a.call(obj, prm1,prm2)
// a.bind(obj,prm1,prm2)()

// arguments是一个对应于传递给函数的参数的类数组对象
// 解法1:
function bindThis(f, oTarget) {
    return function(){
        return f.apply(oTarget, arguments)
    }  
}

//解法2:
function bindThis(f, oTarget) {
    return function(){
        return f.call(oTarget,...arguments)
    }
}
// 解法3
function bindThis(f, oTarget) {
    return f.bind(oTarget)
}
FED2 获取url参数

题目:获取 url 中的参数

  1. 指定参数名称,返回该参数的值 或者 空字符串
  2. 不指定参数名称,返回全部的参数对象 或者 {}
  3. 如果存在多个同名参数,则返回数组
function getUrlParam(sUrl, sKey) {
    var param = sUrl.split('#')[0].split('?')[1];
    if (sKey){//指定参数名称
        var strs = param.split('&');
        var arrs = new Array();//如果存在多个同名参数,则返回数组
        for(var i = 0, len = strs.length; i < len; i++){
            var tmp = strs[i].split('=');
            if(tmp[0] == sKey){
                arrs.push(tmp[1]);
            }
        }
        if (arrs.length == 1){//返回该参数的值或者空字符串
            return arrs[0];
        } else if (arrs.length == 0){
            return "";
        } else {
            return arrs;
        }
    } else {//不指定参数名称,返回全部的参数对象 或者 {}
        if(param == undefined || param == ""){
            return {};
        } else {
            var strs = param.split('&');
            var arrObj = new Object();
            for(var i = 0, len = strs.length; i < len; i++){
                var tmp = strs[i].split('=');
                if (!(tmp[0] in arrObj)) {
                    arrObj[tmp[0]] = [];
                }
                arrObj[tmp[0]].push(tmp[1]);               
            }
            return arrObj;
        }
    }
}

// 解法2(本地测试用例都能过,提交代码就不行,可能语法太新?思路和解法一基本一致,就是有些写法进行了优化)
function getUrlParam(sUrl, sKey) {
  let [, params] = sUrl.split("?");
  if (params) {
    let [param] = params.split("#");
    let paramArr = param.split("&");
    let paramMap = paramArr.map((item) => {
      const [key, value] = item.split("=");
      return { [`${key}`]: value };
    });
    if (!sKey) {
      return paramMap;
    } else {
      let arr = [];
      for (let i of paramMap) {
        console.log(i);
        if (Object.keys(i)[0] === sKey) {
          arr.push(Object.values(i)[0]);
        }
      }
      console.log(arr);
      if (arr.length === 0) return "";
      if (arr.length === 1) return arr[0];

      return arr;
    }
  } else {
    return {};
  }
}
FED3 dom节点查找

题目:查找两个节点的最近的一个共同父节点,可以包括节点自身

// Node.contains()返回的是一个布尔值,表示传入的节点是否为该节点的后代节点
function commonParentNode(oNode1, oNode2) {
    if(oNode1.contains(oNode2)){
        return oNode1;
    }else{
      // 递归查找,不是本人,就往父节点上走,找到之后就是最近的那个
        return commonParentNode(oNode1.parentNode,oNode2);
    }
}
FED4 根据包名,在指定空间中创建对象

题目:根据包名,在指定空间中创建对象

// input:namespace({a: {test: 1, b: 2}}, 'a.b.c.d')
// output:{a: {test: 1, b: {c: {d: {}}}}}

// hasOwnProperty()返回布尔值,判断对象自身属性中是否具有指定的属性
// 解法1
function namespace(oNamespace, sPackage) {
   const packageArr = sPackage.split('.');
    let len = packageArr.length;
    let currObj = oNamespace;
    for (let i = 0; i < len; i++){
        if(!currObj.hasOwnProperty(packageArr[i])){
            currObj[packageArr[i]]={};
            currObj = currObj[packageArr[i]]
        }
    }
    return oNamespace
}

// 解法2——递归
unction namespace(oNamespace, sPackage) {
   if(sPackage.length <= 0) return;
    // var arr = sPackage.split('.');
    var pointer = oNamespace;
        if(sPackage[0] in oNamespace) {
            if(typeof oNamespace[sPackage[0]] !== "object") {
                oNamespace[sPackage[0]] = {};              
            }  
        } else {
            oNamespace[sPackage[0]] = {};
        }
        oNamespace = oNamespace[sPackage[0]];
        namespace(oNamespace, sPackage.slice(2));  
    return pointer;
}
FED5 数组去重

题目:为 Array 对象添加一个去除重复项的方法

// 去重方法有很多种,可以按照需要使用
// 解法1——中规中矩遍历

Array.prototype.uniq = function () {
   var resArr = [];
   var flag = true; 
   for(var i=0;i<this.length;i++){
       if(resArr.indexOf(this[i]) == -1){
           if(this[i] != this[i]){   //排除 NaN
              if(flag){
                   resArr.push(this[i]);
                   flag = false;
              }
           }else{
                resArr.push(this[i]);
           }
       }
   }
    return resArr;
}

// 解法2 
// set是一个不包含重复元素的类数组,因此可以用set类型进行简单的数组去重
Array.prototype.uniq = function () {
    return Array.from(new Set(this))
}

// 解法3
// Array.filter()返回满足筛选条件的数组
// indexOf()获取某个元素第一次出现时的索引
Array.prototype.uniq = function () {
    return this.filter((item,index)=>this.indexOf(item) === index)
}

// 解法4 
// reduce 迭代器,对每个元素执行自定义的reducer函数,并返回汇总结果
// reduce的第二个参数可选,默认为数组的第一个值
Array.prototype.uniq = function () {
    return this.reduce((pre,curr)=>{
      pre.includes(curr)?'':pre.push(curr);return pre
    },[])
}

// 扩展:对象数组去重
let obj = {};
arr.reduce((pre,curr)=>{
    obj[curr.key] ? '': obj[curr.key]=true && pre.push(curr)
    return pre
},[]) 
FED6 斐波那契数列

题目:用 JavaScript 实现斐波那契数列函数,返回第n个斐波那契数。 f(1) = 1, f(2) = 1 等

// 这是递归和动归的经典题目(还有一个汉诺塔问题,两题都完全弄懂,递归和动归也就算是入门了)
// 解法1-递归
function fibonacci(n) {
    if(n==1||n==2)
        return 1;
    return fibonacci(n-1)+fibonacci(n-2);
}

// 解法2-动归
function fibonacci(n) {
    if(!n) return;
    if(n === 1)return 1;
    if(n === 2) return 1;
    let arr = [1,1];
    for(let i = 2; i < n; i++){
        let temp = arr[i-1]+arr[i-2];
        arr.push(temp)
    }
    return arr[n-1]
}
// 解法3-动归2
function fibonacci(n) {
    var a = 1;
    var b = 1;
    var c = 0;
    if(n == 1|| n == 2){
            return 1;
    }else{
           for(var i = 1;i<n-1;i++){
            c = a+b;
            a = b;
            b = c;
        }
            return b;
    }
FED7 时间格式化输出

题目:按所给的时间格式输出指定的时间
格式说明
对于 2014.09.05 13:14:20
yyyy: 年份,2014
yy: 年份,14
MM: 月份,补满两位,09
M: 月份, 9
dd: 日期,补满两位,05
d: 日期, 5
HH: 24制小时,补满两位,13
H: 24制小时,13
hh: 12制小时,补满两位,01
h: 12制小时,1
mm: 分钟,补满两位,14
m: 分钟,14
ss: 秒,补满两位,20
s: 秒,20
w: 星期,为 [‘日’, ‘一’, ‘二’, ‘三’, ‘四’, ‘五’, ‘六’] 中的某一个,本 demo 结果为 五

// Date类型的掌握,记住月份比较特殊,月份是从0开始计数的,所以getMonth()获取当前月份之后要加1
// replace方法中第二个参数里的$1(还会有$2,$3……,$n)表示的是正则里第n个小括号里的内容
// 解法1
function formatDate(t,str){
  let obj = {
    yyyy:t.getFullYear(),
    yy:(""+ t.getFullYear()).slice(-2),
    M:t.getMonth()+1,
    MM:("0"+ (t.getMonth()+1)).slice(-2),
    d:t.getDate(),
    dd:("0" + t.getDate()).slice(-2),
    H:t.getHours(),
    HH:("0" + t.getHours()).slice(-2),
    h:t.getHours() % 12,
    hh:("0"+t.getHours() % 12).slice(-2),
    m:t.getMinutes(),
    mm:("0" + t.getMinutes()).slice(-2),
    s:t.getSeconds(),
    ss:("0" + t.getSeconds()).slice(-2),
    w:['日', '一', '二', '三', '四', '五', '六'][t.getDay()]
  };
  return str.replace(/([a-z]+)/ig,($1)=>obj[$1]);
}
FED8 获取字符串的长度

题目:如果第二个参数 bUnicode255For1 === true,则所有字符长度为 1
否则如果字符 Unicode 编码 > 255 则长度为 2

// 解法1
function strLength(s, bUnicode255For1) {
    if(bUnicode255For1){
        return s.length;
    } else {
        var len = s.length;
        for(var i = 0; i < s.length; i++){
            if(s.charCodeAt(i) > 255){
                len ++;
            }
        }
        return len;
    }
}
// 解法2——把代码简化了一下
function strLength(s, bUnicode255For1) {
    if(bUnicode255For1) return s.length;
    let len = s.length
    for(let i in s){
        if(s.charCodeAt(i)>255){
            len +=1
        }
    }
    return len
}
FED9 邮箱字符串判断

题目:判断输入是否是正确的邮箱格式

// 正则表达式中,\w代表的是0-9,a-z,A-Z以及下划线(_)的集合
// ^是开始符号,$是结束符号
// *代前面括号内的部分可出现0次或n次
function isAvailableEmail(sEmail) {
    var a = /^(\w)+(\.\w+)*@(\w)+(\.\w+)+$/;
        return a.test(sEmail);
}
FED10 颜色字符串转换

题目:将 rgb 颜色字符串转换为十六进制的形式,如 rgb(255, 255, 255) 转为 #ffffff

  1. rgb 中每个 , 后面的空格数量不固定
  2. 十六进制表达式使用六位小写字母
  3. 如果输入不符合 rgb 格式,返回原始输入
// (.+?)属于惰性匹配
// 贪婪匹配是看整个字符串是否匹配,如果不匹配,会去掉最后一个字符,再次尝试还不匹配,继续去掉当前最后一个,直到发现匹配或不剩任何字符(.+)
// 惰性匹配是从左向右匹配, 先看第一个字符是不是匹配, 如果是就加入下一个字符匹配, 直到发现匹配...
// num.toString() 返回转换后的字符串,如果是数字,不传参默认为十进制,传入的数字代表转换之后的进制数
function rgb2hex(sRGB) {
    let reg = /rgb\((.+?)\)/g;
  if (!reg.test(sRGB)) return sRGB;

  let arr = sRGB
    .replace(/rgb\((.+?)\)/g, "$1")
    .replace(/\s/g, "")
    .split(",");
  if (arr.length !== 3) return sRGB;
  let arrStr = [];
  for (let i = 0; i < 3; i++) {
    let num = +arr[i];
    if (isNaN(num)) return sRGB;
    if (num > 255 || num < 0) return sRGB;
    arrStr.push(num.toString(16) === '0'?'00':num.toString(16));
  }
  return "#" + arrStr.join("");
}
FED11 将字符串转换为驼峰格式

题目:以 - 为分隔符,将第二个起的非空单词首字母转为大写

  1. -webkit-border-image 转换后的结果为 webkitBorderImage
// replace()第一个参数是匹配到的字符,第二个参数是对应的索引
function cssStyle2DomStyle(sName) {
    return sName.replace(/\-[a-z]/g,function(a,b){
        return b == 0 ? a.replace('-','') : a.replace('-','').toUpperCase();
    });
}
FED12 字符串字符统计

题目:统计字符串中每个字符的出现频率,返回一个 Object,key 为统计字符,value 为出现频率

// 解法1
function count(str) {
    var a = {};
    var b = str.replace('/\s+/g','').split("");
    for(var i=0; i < b.length; i++){
        if(a[b[i]]!= null){
            a[b[i]]++;
        } else {
            a[b[i]] = 1;
        }
    }return a;
}

// 解法2——合理利用replace的回调函数
function count(str) {
    let obj = {};
    str.replace(/\s/g,'').replace(/./g,(i)=>obj[i]? obj[i]+=1:obj[i]=1);
    return obj
}
FED13 加粗文字

题目:使用一个标签将“牛客网”三个字加粗显示

<p>
  <strong>牛客网</strong>
</p>
FED14 段落标识

题目:请将下面这句话以段落的形式展示在浏览器中——“牛客网是一个专注于程序员的学习和成长的专业平台。”

<p>牛客网是一个专注于程序员的学习和成长的专业平台。</p>
FED15 设置文字颜色

题目:请使用嵌入样式将所有p标签设置为红色文字

<p>欢迎来到牛客网</p>
<p>在这里,我们为你提供了IT名企的笔试面试题库</p>
<p>在这里,我们以题会友</p>
<p>QQ群号:272820159</p>

// css
p {
    color:red
}
FED16 查找数组元素位置

题目:找出元素 item 在给定数组 arr 中的位置,返回索引或者-1

// 有个内置函数,叫indexOf,就是干这个的
function indexOf(arr, item) {
    let len = arr.length;
    if(!len) return -1;
    for(let i =0; i < len; i++){
        if(arr[i]===item){
            return i
        }
    }
    return -1
}
FED17 数组求和

题目:计算给定数组 arr 中所有元素的总和

// 还记得之前说的reduce么,人家就是专业干这个的
// 解法1
function sum(arr) {
    return arr.reduce((pre,curr)=>pre+curr)
}

// 解法2——原始的迭代
function sum(arr) {
    var a = 0;
    for(var i = 0; i < arr.length; i++){
        a += arr[i]
    }
    return a;
}
FED18 移除数组中的元素

题目:移除数组 arr 中的所有值与 item 相等的元素。不要直接修改数组 arr,结果返回新的数组

// 解法1——常规遍历
function remove(arr, item) {
    var a = [];
    for(var i = 0; i < arr.length; i++){
        if(arr[i] === item){
            continue;
        } else {
            a.push(arr[i])
        }
    }
    return a;
}
// 解法2——reduce
function remove(arr, item) {
   return arr.reduce((pre,curr)=>{
       if(curr !== item){
           pre.push(curr)
       }
       return pre
   },[])
}
FED19 移除数组中的元素2

题目:移除数组 arr 中的所有值与 item 相等的元素,直接在给定的 arr 数组上进行操作,并将结果返回

// splice()通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组
function removeWithoutCopy(arr, item) {
    let len = arr.length
    for (let i = len; i >= 0; i--){
        if(arr[i] === item){
            arr.splice(i,1);
        }
    }
    return arr;
}
FED20 添加元素1

题目:在数组 arr 末尾添加元素 item。不要直接修改数组 arr,结果返回新的数组

// concat()合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
// 解法1
function append(arr, item) {
return arr.concat(item)
}

// push 将一个或多个元素添加到数组的末尾,并返回该数组的新长度
// 解法2
function append(arr, item) {
    var arr_1 = [];
    for(var i = 0; i < arr.length; i++){
        arr_1.push(arr[i]);
    }
    arr_1.push(item);
    return arr_1;
}
FED21 删除数组最后一个元素

题目:删除数组 arr 最后一个元素。不要直接修改数组 arr,结果返回新的数组

// slice()返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变
// 解法1
function truncate(arr) {
    return arr.slice(0,-1)
}

// 解法2 原始遍历
function truncate(arr) {
    var arr_1 = [];
    for(var i = 0; i < arr.length-1; i++){
        arr_1.push(arr[i])
    }
    return arr_1;
}
FED22 添加元素2

题目:在数组 arr 开头添加元素 item。不要直接修改数组 arr,结果返回新的数组

// 和之前题目一样,可以使用concat
// 解法1
function prepend(arr, item) {
   return [item].concat(arr)
}

// unshift()在调用它的类数组对象的开始位置插入给定的参数
// 解法2
function prepend(arr, item) {
    var arr_1 = [];
    for(var i = 0; i < arr.length; i++){
        arr_1.push(arr[i])
    }
    arr_1.unshift(item);
    return arr_1;
}
FED23 删除数组第一个元素

题目:删除数组 arr 第一个元素。不要直接修改数组 arr,结果返回新的数组

// 解法1
function curtail(arr) {
    return arr.slice(1)
}
// shift()从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度
 // 解法2
function curtail(arr) {
    var arr_1 = [];
    for(var i = 0; i < arr.length; i++){
        arr_1.push(arr[i])
    }
    arr_1.shift();
    return arr_1;
}
FED24 数组合并

题目:合并数组 arr1 和数组 arr2。不要直接修改数组 arr,结果返回新的数组

// 这道题考的就是concat的原理
// 解法1
function concat(arr1, arr2) {
var arr_1 = [];
    for(var i = 0; i < arr1.length; i++){
        arr_1.push(arr1[i])
    }
    for(var i = 0; i < arr2.length; i++){
        arr_1.push(arr2[i])
    }
    return arr_1;
}

// 解法2
function concat(arr1, arr2) {
    return arr1.concat(arr2)
}
FED25 添加元素3

题目:在数组 arr 的 index 处添加元素 item。不要直接修改数组 arr,结果返回新的数组

// splice()通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
// 简单数组的深拷贝常用方法:arr.slice(0),arr.concat()
// 解法1
function insert(arr, item, index) {
   let arr1 = arr.concat(); // =arr.slice(0)
   arr1.splice(index,0,item);
    return arr1
}
// 解法2
function insert(arr, item, index) {
    let arr1 = arr.slice(0,index);
    let arr2 = arr.slice(index);
   return arr1.concat(item,arr2)
}
FED26 计数

题目:统计数组 arr 中值等于 item 的元素出现的次数

// filter()就是做这个的
// 解法1
function count(arr, item) {
    let filterArr = arr.filter(i=>i === item);
    return filterArr.length
}

// 解法2——可以reduce模拟
function count(arr, item) {
    var count = arr.reduce(function(prev, curr){
        return curr === item ? prev+1 : prev
    },0);
    return count
}

// 原始的遍历
function count(arr, item) {
    var count = 0;
    for(var i = 0; i <arr.length; i++){
        if(arr[i] === item){
            count++
        }
    }
    return count;
}
FED27 查找重复元素

题目:找出数组 arr 中重复出现过的元素

// 利用forEach进行遍历
// indexOf()元素第一次出现的位置
// lastIndexOf()元素最后一次出现的位置
// 解法1
function duplicates(arr) {
   var arr_1 = [];
    arr.forEach(function(item){
        if(arr.indexOf(item) != arr.lastIndexOf(item)&& arr_1.indexOf(item) == -1){
            arr_1.push(item)
        }
    })
    return arr_1;
}
// 解法2——把去重反过来做,然后再去重(因为重复元素可能有多个)
function duplicates(arr) {
 let arr1= arr.filter((item,index)=>arr.indexOf(item)!== index)
 return Array.from(new Set(arr1))
}
FED28 求二次方

题目:为数组 arr 中的每个元素求二次方。不要直接修改数组 arr,结果返回新的数组

// map()创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值
// 解法1
function square(arr) {
    return arr.map(item=>item*item)
}
FED29 查找元素位置

题目:在数组 arr 中,查找值与 item 相等的元素出现的所有位置

// 解法1——乖乖的遍历
function findAllOccurrences(arr, target) {
    let indexArr = [];
    let len = arr.length;
    for(let i = 0; i < len; i++){
        if(arr[i] === target){
            indexArr.push(i)
        }
    }
    return indexArr
}
FED30 避免全局变量

题目:给定的 js 代码中存在全局变量,请修复

// var 声明语句声明一个变量,并可选地将其初始化为一个值,存在变量提升(变量和函数的声明会在物理层面移动到代码的最前面)
// let 声明一个块级作用域的本地变量,并且可选的将其初始化为一个值
// const 声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。
// let和const不会进行变量提升,所以如果在声明之前就调用的话,就会报无法访问的错误,这种情况就被成为暂时性死区
// 原题
function globals() {
    myObject = {
      name : 'Jory'
    };

    return myObject;
}
// 修改后
function globals() {
   let myObject = {
      name : 'Jory'
    };

    return myObject;
}
FED31 正确的函数定义

题目:请修复给定的 js 代码中,函数定义存在的问题

// 原题
function functions(flag) {
    if (flag) {
      function getValue() { return 'a'; }
    } else {
      function getValue() { return 'b'; }
    }

    return getValue();
}
// 分析:这道题getValue被重复声明了,所以运行之后下面的getValue会把上面的给覆盖掉,不管怎么样都会输出'b'

// 解法1
function functions(flag) {
    if (flag) {
      let getValue = function getValue() { return 'a'; }
    } else {
      let getValue = function getValue() { return 'b'; }
    }
 
    return getValue();
}
// 解法2——用三目运算符优化
function functions(flag) {
    return flag ? 'a':'b'
}
FED32 正确的使用parseInt

题目:修改 js 代码中 parseInt 的调用方式,使之通过全部测试用例

// parseInt(string, radix),解析一个字符串并返回指定基数的十进制整数, radix 是2-36之间的整数,表示被解析字符串的基数,遇到不是数字的字符串就不解析了,比如'12px'->12
// 原题
function parse2Int(num) {
    return parseInt(num);
}
// 解法1
function parse2Int(num) {
    return parseInt(num,10);
}
FED33 完全等同

题目:判断 val1 和 val2 是否完全等同

// 考察'==='和'=='的区别
// 解法1
function identity(val1, val2) {
return val1===val2
}
FED34 计时器

题目:实现一个打点计时器,要求
1、从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 1
2、返回的对象中需要包含一个 cancel 方法,用于停止定时操作
3、第一个数需要立即输出

// setInterval重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟
// clearInterval可取消先前通过 setInterval() 设置的重复定时任务。
// 解法1
function count(start, end) {
    console.log(start++);
    let timer = setInterval(()=>{
        if(start <= end){
           console.log(start++)
        }
    },100) 
    return {
        cancel:()=>clearInterval(timer)
    }
}
FED35 流量控制

题目:实现 fizzBuzz 函数,参数 num 与返回值的关系如下:
1、如果 num 能同时被 3 和 5 整除,返回字符串 fizzbuzz
2、如果 num 能被 3 整除,返回字符串 fizz
3、如果 num 能被 5 整除,返回字符串 buzz
4、如果参数为空或者不是 Number 类型,返回 false
5、其余情况,返回参数 num

// 解法1
function fizzBuzz(num) {
    if(num%3 == 0 && num%5 == 0){
        return 'fizzbuzz';
    } else if(num%3 == 0){
        return 'fizz';
    }else if(num%5 == 0){
        return 'buzz';
    }else if(num == null || typeof num!= 'number'){
        return false;
    }else {
        return num
    }
}

// 解法2
function fizzBuzz(num) {
    if(isNaN(num)) return false;
    if(num % 15){
        if(!(num %3))return 'fizz';
        if(!(num %5))return 'buzz';
        return num
    } else {
        return 'fizzbuzz'
    }
}
FED36 函数传参

题目:将数组 arr 中的元素作为调用函数 fn 的参数

// 考察的还是call,bind和apply
// 解法1
function argsAsArray(fn, arr) {
return fn.call(this,...arr)
}

// 解法2
function argsAsArray(fn, arr) {
   return fn.apply(this, arr)
}

// 解法3
function argsAsArray(fn, arr) {
   return fn.bind(this, ...arr)()
}

FED37 函数的上下文

题目:将函数 fn 的执行上下文改为 obj 对象

// 解法1
function speak(fn, obj) {
	return fn.apply(obj)
}
// 解法2
function speak(fn, obj) {
    return fn.bind(obj)();
}
// 解法3
function speak(fn, obj) {
	return fn.call(obj)
}
FED38 返回函数

题目:实现函数 functionFunction,调用之后满足如下条件:
1、返回值为一个函数 f
2、调用返回的函数 f,返回值为按照调用顺序的参数拼接,拼接字符为英文逗号加一个空格,即 ', ’
3、所有函数的参数数量为 1,且均为 String 类型

// 高阶函数是一个接收函数作为参数或将函数作为输出返回的函数
// 函数可返回一个函数,e.g. func(a)(b),先执行func(a),然后将b赋予返回的函数作为入参
function functionFunction(str) {
    return str1=>(str+', '+str1)
}
FED39 使用闭包

题目:实现函数 makeClosures,调用之后满足如下条件:
1、返回一个函数数组 result,长度与 arr 相同
2、运行 result 中第 i 个函数,即 result[i] (),结果与 fn(arr[i]) 相同

// 闭包:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)
//JS 引擎判断当前是一个闭包时,就会在堆空间创建换一个“closure(foo)”的对象(这是一个内部对象,JS 是无法访问的),用来保存对应变量
// 解法1
function makeClosures(arr, fn) {
    return arr.map(item=>fn.bind(null,item))
}

// 解法2
function makeClosures(arr, fn) {
    var result = [];
    for(var i = 0; i < arr.length; i++){
        result[i] = fn.bind(null, arr[i])
    }
    return result;
}
// 解法3
function makeClosures(arr, fn) {
    var result = [];
    for(var i = 0; i < arr.length; i++){
        result[i] = function(num){
            return function(){
                return fn(num);
            }
        }(arr[i]);
    }
    return result;
}
FED40 二次封装函数

题目:已知函数 fn 执行需要 3 个参数。请实现函数 partial,调用之后满足如下条件:
1、返回一个函数 result,该函数接受一个参数
2、执行 result(str3) ,返回的结果与 fn(str1, str2, str3) 一致

// 这是闭包和bind,call,apply家族的合作
// 解法1
function partial(fn, str1, str2) {
	return function(str3){
    return fn.bind(this,str1, str2,str3)()
	}
}
 // call和apply的懒得写了。。。
FED41 使用arguments

题目:函数 useArguments 可以接收 1 个及以上的参数。请实现函数 useArguments,返回所有调用参数相加后的结果。本题的测试参数全部为 Number 类型,不需考虑参数转换。

// arguments是一个类数组,所以需要先将它转化为数组,转化的方法利用数组拷贝(详情看FED25 )
// arguments是一个对应于传递给函数的参数的类数组对象
// 解法1
function useArguments() {
    return [...arguments].reduce((pre,curr)=>pre+curr)
}

// 解法2
function useArguments() {
    var sum = 0;
    for(var i = 0; i < arguments.length; i++){
        sum += arguments[i]
    }
    return sum;
}
FED42 使用apply调用函数

题目:实现函数 callIt,调用之后满足如下条件
1、返回的结果为调用 fn 之后的结果
2、fn 的调用参数为 callIt 的第一个参数之后的全部参数

// 思路:将arguments的第一个值去掉,剩余值用appply返回
// 解法1
function callIt(fn) {
    const param = [...arguments].slice(1);
    return fn.apply(null,param)
}
// call和bind类似,就不写了
FED43 二次封装函数2

题目:实现函数 partialUsingArguments,调用之后满足如下条件:
1、返回一个函数 result
2、调用 result 之后,返回的结果与调用函数 fn 的结果一致
3、fn 的调用参数为 partialUsingArguments 的第一个参数之后的全部参数以及 result 的调用参数

// 思路:和上一题差不多,只不过一个返回结果,一个返回函数
// 解法1
function partialUsingArguments(fn) {
  
    const param = [...arguments].slice(1);
    return function(){
       return fn.apply(null,param.concat([...arguments]))
    }
}
FED44 柯里化

题目:已知 fn 为一个预定义函数,实现函数 curryIt,调用之后满足如下条件:
1、返回一个函数 a,a 的 length 属性值为 1(即显式声明 a 接收一个参数)
2、调用 a 之后,返回一个函数 b, b 的 length 属性值为 1
3、调用 b 之后,返回一个函数 c, c 的 length 属性值为 1
4、调用 c 之后,返回的结果与调用 fn 的返回值一致
5、fn 的参数依次为函数 a, b, c 的调用参数

// 柯里化:把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数并返回结果的新函数,简单的说就是把多个入参的函数改为每次都单一入参,返回新入参的函数再不断调用
// 柯里化的优点是能够进行参数复用,能提前确认(不同if条件下可以返回不同的函数)

// 解法1
function curryIt(fn) {
return function(a){
    return function(b){
        return function(c){
            return fn(a,b,c)
        }
    }
}
}
FED45 或运算

题目:返回参数 a 和 b 的逻辑或运算结果

// 运算符'||'会先计算左操作数的值,如果是true,返回左操作数,如果是false,返回右操作数的结果
function or(a, b) {
	return a || b
}
FED46 且运算

题目:返回参数 a 和 b 的逻辑且运算结果

// 运算符'&&'会先计算左操作数的值,如果是false,直接返回false,是true将右操作数的结果返回(这种行为被称为短路)
function and(a, b) {
	return a&&b
}

// 补充知识点:运算符'??'(??)是一个逻辑操作符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数
FED47 模块

题目:完成函数 createModule,调用之后满足如下要求:
1、返回一个对象
2、对象的 greeting 属性值等于 str1, name 属性值等于 str2
3、对象存在一个 sayIt 方法,该方法返回的字符串为 greeting属性值 + ', ’ + name属性值

// 这道题考查的是this的用法,this通常指向当前执行上下文
// 解法1
function createModule(str1, str2) {
    let obj = {
        greeting : str1,
        name : str2,
        sayIt : function(){
            return this.greeting + ', ' + this.name
        }
    }
    return obj;

}
FED48 二进制转换

题目:获取数字 num 二进制形式第 bit 位的值。注意:
1、bit 从 1 开始
2、返回 0 或 1
3、举例:2 的二进制为 10,第 1 位为 0,第 2 位为 1

// ‘>>’ 右移(带符号位),第一个操作数是正数,最高位补0,是负数,补1
// & 进行布尔与(AND)运算
// 解法1
function valueAtBit(num, bit) {
	return (num>>(bit-1))&1
}
FED49 二进制转换2

题目:给定二进制字符串,将其换算成对应的十进制数字

// parseInt的详细说明看FED32
// 解法1
function base10(str) {
	return parseInt(str,2)
}
FED50 二进制转换3

题目:将给定数字转换成二进制字符串。如果字符串长度不足 8 位,则在前面补 0 到满8位。

// 解法1 愉快的遍历
function convertToBinary(num) {
    var a = num.toString(2);
    while (a.length < 8){
        a = "0" + a
    }
    return a;
}
// padStart()方法用另一个字符串填充当前字符串(如果需要的话,会重复多次),以便产生的字符串达到给定的长度。从当前字符串的左侧开始填充
// 解法2(可能因为api太新,校验不通过)
function convertToBinary(num) {
    let a = num.toString(2)
    return a.padStart('8','0')
}
FED51 乘法

题目:求 a 和 b 相乘的值,a 和 b 可能是小数,需要注意结果的精度问题

// toFixed()方法使用定点表示法来格式化一个数值,返回对应字符串
// 解法1
function multiply(a, b) {
    var aDec = a.toString().split('.')[1] || '';
    var bDec = b.toString().split('.')[1] || '';
    var fix = aDec.length  + bDec.length;
    return (a * b).toFixed(fix);
}
FED52 改变上下文

题目:将函数 fn 的执行上下文改为 obj,返回 fn 执行后的值

// 又是对call,appply和bind的复习
// 解法1
function alterContext(fn, obj) {
		return fn.call(obj)
}
// 其余俩自己补充吧~
FED53 批量改变对象的属性

题目:给定一个构造函数 constructor,请完成 alterObjects 方法,将 constructor 的所有实例的 greeting 属性指向给定的 greeting 变量。

// 只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性就会指向函数的原型对象,同时prototype会有一个constructor属性,会指向prototype属性所在函数的指针
// 解法1
function alterObjects(constructor, greeting) {
constructor.prototype.greeting = greeting
}
FED54 属性遍历

题目:找出对象 obj 不在原型链上的属性(注意这题测试例子的冒号后面也有一个空格~)
1、返回数组,格式为 key: value
2、结果数组不要求顺序

// for in遍历获取的是obj上所有可遍历的属性,包括自有的和原型链上的
// hasOwnProperty获取的是当前对象自有的属性
// 解法1
function iterate(obj) {
    const arr = [];
    for(let i in obj){
        if(obj.hasOwnProperty(i)){
            arr.push(`${i}: ${obj[i]}`)
        }
    }
    return arr
}
FED55 判断是否包含数字

题目:给定字符串 str,检查其是否包含数字,包含返回 true,否则返回 false

// 这道题就是正则的基础,/d代表匹配的是0-9
// test()方法返回布尔值,判断字符串中是否匹配正则
// 解法1
function containsNumber(str) {
    return /\d/.test(str)
}
FED56 检查重复字符串

题目:给定字符串 str,检查其是否包含连续重复的字母(a-zA-Z),包含返回 true,否则返回 false

// / \1表示括号里匹配的内容,这样相当于判断前一个和后一个一致
// 解法1
function containsRepeatingLetter(str) {
    return /([a-zA-Z])\1/.test(str)
}
FED57 判断是否以元音字母结尾

题目:给定字符串 str,检查其是否以元音字母结尾
1、元音字母包括 a,e,i,o,u,以及对应的大写
2、包含返回 true,否则返回 false

// '$'表示字符串的结尾
// []表示匹配的字符范围
// 解法1
function endsWithVowel(str) {
    return /[a,e,i,o,u]$/i.test(str)
}
FED58 获取指定字符串

题目:给定字符串 str,检查其是否包含 连续3个数字
1、如果包含,返回最先出现的 3 个数字的字符串
2、如果不包含,返回 false

// match()表示正则表达式是否在字符串中被匹配上,返回符合匹配的字符串数组,如果没有匹配,返回null
// 解法1
function captureThreeNumbers(str) {
 let arr = str.match(/\d{3}/);
    return arr ? arr[0]:false
}
FED59 判断是否符合指定格式

题目:给定字符串 str,检查其是否符合如下格式
1、XXX-XXX-XXXX
2、其中 X 为 Number 类型

// 解法1
function matchesPattern(str) {
    return /^\d{3}-\d{3}-\d{4}$/.test(str)
}
FED60 判断是否符合USD格式

题目:给定字符串 str,检查其是否符合美元书写格式
1、以 $ 开始
2、整数部分,从个位起,满 3 个数字用 , 分隔
3、如果为小数,则小数部分长度为 2
4、正确的格式如:$1,023,032.03 或者 $2.03,错误的格式如:$3,432,12.12 或者 $34,344.3

// 这道题考的是正则的综合应用,涉及比较多的知识点,对正则熟练的话做出来也不难
function isUSD(str) {
return /^\$\d{1,3}(,\d{3})*(\.\d{2})?$/.test(str)
}
后记

在大约三年前,刚准备入行的时候把这60道题做了一次,三年后已经完全忘光光了,在重新复习(学习)的过程中,发现这三年自己的知识体系有了进步,前端也在不断的发展,不断涌现新知识。这些题其实难度都不大,主要考验的是对js基础的掌握程度,做这种题目更多的是查漏补缺,巩固知识点,越是简单的东西,越需要扎扎实实的学~

所以,还是要不断努力,不断学习哇

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值