前端JS编程题

1.题目描述 找出数组 arr 中重复出现过的元素 输入 [1, 2, 4, 4, 3, 3, 1, 5, 3] 输出 [1, 3, 4]

function duplicates() {
var arr = [1, 2, 4, 4, 3, 3, 1, 5, 3]
var temp = [];
arr.forEach(function(elem){
if(arr.indexOf(elem) != arr.lastIndexOf(elem) && temp.indexOf(elem) == -1){
temp.push(elem);
}
});
return temp;
}

做法:
1.找出重复的数组项‘[1,4,4,3,3,1,3]
2.在重复的数组项中做一个去重,筛选出[1,3,4]

2.数组去重

1.它是最简单的数组去重方法(indexOf方法)

实现思路:新建一个数组,遍历去要重的数组,当值不在新数组的时候(indexOf为-1)就加入该新数组中;
var arr=[2,8,5,0,5,2,6,7,2];
function unique1(arr){
var hash=[];
for (var i = 0; i < arr.length; i++) {
if(hash.indexOf(arr[i])==-1){
hash.push(arr[i]);
}
}
return hash;
}

2.数组下标判断法

调用indexOf方法,性能和方法1差不多

实现思路:如果当前数组的第 i 项在当前数组中第一次出现的位置不是 i,那么表示第 i 项是重复的,忽略掉。否则存入结果数组。

function unique2(arr){
var hash=[];
for (var i = 0; i < arr.length; i++) {
if(arr.indexOf(arr[i])==i){
hash.push(arr[i]);
}
}
return hash;
}

3.题目描述 给定字符串 str,检查其是否符合如下格式

		1、XXX-XXX-XXXX 2、其中 X 为 Number 类型
    首先:(\d{3}-) 重复了两次,所以是(\d{3}-){2}。即XXX-XXX-。
    然后:d{4}。即XXXX。
    最后,全局匹配,所以加上开始符^和结束符$。
    */
    function matchesPattern(str) {
        var res=/^(\d{3}\-){2}\d{4}$/;
        return  res.test(str);
    }
    console.log(matchesPattern('800-555-1212'));
	

## 4.给定字符串 str,检查其是否包含 连续3个数字 1、如果包含,返回最先出现的 3 个数字的字符串 2、如果不包含,返回 false
function captureThreeNumbers(str) {
  var reg;
  if(reg = str.match(/(\d{3})/)){
    return reg[0];
  }else{
    return false;
  }
}

## 5.题目描述 找出元素 item 在给定数组 arr 中的位置 输出描述: 如果数组中存在 item,则返回元素在数组中的位置,否则返回 -1
function indexOf(arr, item) {
  if (Array.prototype.indexOf){   //判断当前浏览器是否支持
      return arr.indexOf(item);
  } else {
      for (var i = 0; i < arr.length; i++){
          if (arr[i] === item){
              return i;
          }
      }
  }     
  return -1;     //总是把return -1暴漏在最外层
}

## 6.题目描述 计算给定数组 arr 中所有元素的总和 输入描述: 数组中的元素均为 Number 类型
function sum(arr) {
    var arr
    var sum = 0
    for(var i =0;i<arr.length;i++){
        sum = sum + arr[i]
    }
    return sum
}
var arr = [1,2,3,4]
var s= sum(arr)
console.log(s)

## 7.题目描述 移除数组 arr 中的所有值与 item 相等的元素,直接在给定的 arr 数组上进行操作,并将结果返回
function removeWithoutCopy(arr, item) {
     for(var i = 0; i < arr.length; i++){
         if(arr[i] == item){
             //splice方***改变数组长度,当减掉一个元素后,后面的元素都会前移,因此需要相应减少i的值
             arr.splice(i,1);
             i--;
         }
     }
     return arr;
 }
var arr = [1, 2, 3, 4, 2]
var s = remove(arr,2)
console.log('s',s)  //1,3,4

## 8.题目描述 移除数组 arr 中的所有值与 item 相等的元素。不要直接修改数组 arr,结果返回新的数组
方法1:
function remove(arr,item) {
			var newArr = []
			for(var i=0;i<arr.length;i++){
				if(arr[i] != item){
					newArr.push(arr[i])
				}
			}
			return newArr 
		}
var arr = [1, 2, 3, 4, 2]
var s = remove(arr,2)
console.log('s',s)

方法2:
function remove(arr,item){
    var newarr = arr.slice(0);   //得到一个和arr一样的数组(拷贝数组)
    for(var i=0;i<newarr.length;i++){
        if(newarr[i] == item){
            newarr.splice(i,1);   //移除位置为i的元素
            i--;   //这里因为有元素的删除所以一定要有i--
        }
    }
    return newarr;
}

## 9.题目描述 在数组 arr 的 index 处添加元素 item。不要直接修改数组 arr,结果返回新的数组
function insert(arr, item, index) {
   var temp = arr.slice(0)
   temp.splice(index,0,item)
  return temp
}

## 10.题目描述 为数组 arr 中的每个元素求二次方。不要直接修改数组 arr,结果返回新的数组
function square(arr) {
		    var newArr = []
		    for(var i =0;i<arr.length;i++){
		       newArr.push(arr[i]*arr[i]);
		    }
		    return newArr
}

## 11.请修复给定的 js 代码中,函数定义存在的问题
function functions(flag) {
    if (flag) {
     var a =  function getValue() { return 'a'; }
    } else {
      var b = function getValue() { return 'b'; }
    }

    return getValue();
}

//else中的语句相当于将if中的function重写,因此无论flag为何值,返回的方法始终为重写后的方法。将方法赋值给一个变量,方法就不会被重写,因此才能得到正确的结果。
修复后:

function functions(flag) {
    if (flag) {
      var getValue = function () { return 'a'; }
    } else {
      var getValue = function () { return 'b'; }
    }
    return getValue();
}

## 12.按10进制去处理字符串,碰到非数字字符,会将后面的全部无视

function parse2Int(num) {
     return parseInt(num,10);
 }

## 13.实现一个打点计时器,要求 1、从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 1     2、返回的对象中需要包含一个 cancel 方法,用于停止定时操作   3、第一个数需要立即输出
function count(start, end) {
		    console.log(start)
		    var timer= setInterval(function () {
		        if (start < end) {
		            console.log(start += 1)
		        }
		    }, 100)
		    return { cancel: function () { clearInterval(timer)} }
}

## 14.将数组 arr 中的元素作为调用函数 fn 的参数

每个函数都包含两个非继承而来的方法:apply()和 call(),这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。
思路:
1.通过fn调用了apply方法,this指向fn,即将数组arr中的元素传递给函数fn作为实参;
2.之所以使用apply方法,是因为apply方法的第二个参数可以是Array的实例(即数组),也可以是arguments对象(类数组)。apply可以将一个数组默认的转换为一个类数组。
使用call方法时传递给函数的参数必须逐个列举出来,不能是一个数组。

function argsAsArray(fn, arr) {
    return fn.apply(this, arr);
}

ES6写法:
function argsAsArray(fn, arr) {
   return fn(...arr);//ES6写法,   ...扩展运算符
}

## 15.将函数 fn 的执行上下文改为 obj 对象
1. 对象冒充
function speak(fn, obj) {
    obj.func = fun;
    var re = obj.func();//原地执行func(),this会变成调用对象obj。
    delete obj.func;
    return re;
}

2. bind
function speak(fn, obj) {
    return fn.bind(obj)();//绑定完成后,自调用
}

3. call
function speak(fn, obj) {
    fn.call(obj);//call第一个参数后是多个fn的入口参数,这里为空
}

4. apply
function speak(fn, obj) {
    fn.apply(obj);//call与apply大致相同,后者只有两个入口参数,一个obj,一个参数数组,这里为空
}

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

// 一个闭包完美解决
function functionFunction(str) {
    return f = function(arr){
        return str+", "+arr;
    }; 
}

分析:
首先返回值是一个函数,所以定义一个函数变量fn
由示例可以看出返回函数的参数为一个字符串(str2)
返回函数fn的返回值为:外面主函数的参数str与返回函数fn的参数str2字符串的拼接,
中间通过", "隔开。
故fn函数:return str + ", " + str2;

## 17.题目描述 实现函数 makeClosures,调用之后满足如下条件: 1、返回一个函数数组 result,长度与 arr 相同 2、运行 result 中第 i 个函数,即 result[i](),结果与 fn(arr[i]) 相同
简单的描述闭包:如果在函数func内部声明函数inner,然后在函数外部调用inner,这个过程即产生了一个闭包。
题目要求的是返回一个函数数组,如果在循环中直接写result[i] = function(){return fn(arr[i]);}或者result.push(function(){return fn(arr[i]);}),最终的结果是不正确的,因为在每次迭代的时候,那样的语句后面的方法并没有执行,只是创建了一个函数体为“return fn(arr[i]);”的函数对象而已,当迭代停止时,i为最终迭代停止的值,在函数被调用时,i依旧为最终迭代停止的值,因此无法返回正确的结果。
为了解决这个问题,需要声明一个匿名函数,并立即执行它。
function(num){return function(){return fn(arr[num]); }; }(i),函数执行后,i立即传入并被内部函数访问到,因此就能得到正确的结果。闭包允许你引用存在于外部函数中的变量。
下面的代码使用的是forEach循环

方法1:
function makeClosures(arr, fn) {
  var result = [];
     arr.forEach(function(e){
         result.push(function(num){
             return function(){
                 return fn(num);
             };
         }(e));
     });
     return result;
 }

方法2:
function makeClosures(arr, fn) {
    var result = [];
    arr.forEach(function(v){
        result.push(fn.bind(null, v));
    })
    return result;
}

方法3:
//参考《JavaScript高级程序设计》的典型方法
function makeClosures(arr, fn) {
    var result = new Array();
    for(var i=0;i<arr.length;i++){
        result[i] = function(num){
            return function(){
                return fn(num);   
            }
        }(arr[i]);
    }
    return result;
}

方法4:
//使用ES5的bind()方法
function makeClosures(arr, fn) {
    var result = new Array();
    for(var i=0;i<arr.length;i++){
        result[i] = fn.bind(null,arr[i]);
    }
    return result;
}

方法5:
//使用forEach()
function makeClosures(arr, fn) {
    var result = new Array();
    arr.forEach(function(curr){
        result.push(function(){return fn(curr)});
    })
    return result;
}

## 18.题目描述 已知函数 fn 执行需要 3 个参数。请实现函数 partial,调用之后满足如下条件: 1、返回一个函数 result,该函数接受一个参数 2、执行 result(str3) ,返回的结果与 fn(str1, str2, str3) 一致
// call和apply必须显式地调用str3,立即执行
// bind不是立即执行,未传入str3时,并未执行,只是返回一个函数,等待参数传入
// this用于上下文不确定的情况
 
// call
function partial(fn, str1, str2) {
    function result(str3) {
        return fn.call(this, str1, str2, str3);
    }
 
     return result;
}
 
// apply(这里只是为了对照)
function partial(fn, str1, str2) {
    function result(str3) {
        return fn.apply(this, [str1, str2, str3]);
    }
 
    return result;
}
 
// 这个bind会生成一个新函数(对象), 它的str1, str2参数都定死了, str3未传入, 一旦传入就会执行
function partial(fn, str1, str2) {
    return fn.bind(this, str1, str2); // 或 return fn.bind(null, str1, str2);
}
 
// bind同上, 多了一步, 把str3传入的过程写在另一个函数里面,
// 而另一个函数也有str1, str2参数
// 此法有种多次一举的感觉,但是表示出了后续的调用。
function partial(fn, str1, str2) {
    function result(str3) {
        return fn.bind(this, str1, str2)(str3);
    }
 
    return result;
}
 
// 匿名函数,默认this绑定global,与bind的第一个参数为this时效果一样。
function partial(fn, str1, str2) {
    return function(str3) {
        return fn(str1, str2, str3);
    }
}
 
// ES6。this指向undefined.
const partial = (fn, str1, str2) => str3 => fn(str1, str2, str3);

## 19.题目描述 函数 useArguments 可以接收 1 个及以上的参数。请实现函数 useArguments,返回所有调用参数相加后的结果。本题的测试参数全部为 Number 类型,不需考虑参数转换。
这里重点就是类数组对象:arguments -- 接收所有传入函数的参数值的类数组对象,它有两个特点跟数组很像,1.可以用下标访问每个元素2.具有length属性。
这里最好先通过Array.prototype.slice.call(我们的类数组对象) 将其转换成一个真正的数组对象,然后再遍历求和即可。
function useArguments() {
var aArguments=Array.prototype.slice.call(arguments);
    var sum=0;
    aArguments.forEach(function(value){
        sum+=value;
    });
    return sum;
}

## 20.题目描述 实现函数 callIt,调用之后满足如下条件 1、返回的结果为调用 fn 之后的结果 2、fn 的调用参数为 callIt 的第一个参数之后的全部参数
function callIt(fn) {
    let args = Array.prototype.slice.call(arguments,1)
    return fn.apply(this,args)
}

## 21.题目描述 实现函数 partialUsingArguments,调用之后满足如下条件: 1、返回一个函数 result 2、调用 result 之后,返回的结果与调用函数 fn 的结果一致 3、fn 的调用参数为 partialUsingArguments 的第一个参数之后的全部参数以及 result 的调用参数
function partialUsingArguments(fn) {
    let args1 = Array.prototype.slice.call(arguments, 1);  //获得函数partialUsingArguments()  除第一个参数外的参数数组
    return function() {
        let args2 = Array.prototype.slice.call(arguments, 0)   //获得函数的参数数组
        return fn.apply(this, args1.concat(args2))
    }
}

## 22.什么是柯里化?
维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

curry的一些性能问题你只要知道下面四点就差不多了:

存取arguments对象通常要比存取命名参数要慢一点
一些老版本的浏览器在arguments.length的实现上是相当慢的
使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点
创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上
其实在大部分应用中,主要的性能瓶颈是在操作DOM节点上,这js的性能损耗基本是可以忽略不计的,所以curry是可以直接放心的使用。

## 23. 实现一个add方法,使计算结果能够满足如下预期:

add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = Array.prototype.slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var _adder = function() {
        _args.push(...arguments);
        return _adder;
    };

    // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return _adder;
}

add(1)(2)(3)                // 6
add(1, 2, 3)(4)             // 10
add(1)(2)(3)(4)(5)         // 15
add(2, 6)(1)                // 9

## 24.题目描述 已知 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 的调用参数
柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。简单理解题目意思,就是指,我们将预定义的函数的参数逐一传入到curryIt中,当参数全部传入之后,就执行预定义函数。于是,我们首先要获得预定义函数的参数个数fn.length,然后声明一个空数组去存放这些参数。返回一个匿名函数接收参数并执行,当参数个数小于fn.length,则再次返回该匿名函数,继续接收参数并执行,直至参数个数等于fn.length。最后,调用apply执行预定义函数。

function curryIt(fn) {
     //获取fn参数的数量
     var n = fn.length;
     //声明一个数组args
     var args = [];
     //返回一个匿名函数
     return function(arg){
         //将curryIt后面括号中的参数放入数组
         args.push(arg);
         //如果args中的参数个数小于fn函数的参数个数,
         //则执行arguments.callee(其作用是引用当前正在执行的函数,这里是返回的当前匿名函数)。
         //否则,返回fn的调用结果
         if(args.length < n){
            return arguments.callee;
         }else return fn.apply("",args);
     }
 }

## 25.题目描述 完成函数 createModule,调用之后满足如下要求: 1、返回一个对象 2、对象的 greeting 属性值等于 str1, name 属性值等于 str2   3、对象存在一个 sayIt 方法,该方法返回的字符串为 greeting属性值 + ', ' + name属性值
function createModule(str1, str2) {
		    var obj = {
		        greeting:str1,
		        name:str2,
		        sayIt : function (){
		            return this.greeting + ', ' + this.name  //注意逗号后面有空格
		        }
		    }
		    return obj
}

## 26.题目描述 求 a 和 b 相乘的值,a 和 b 可能是小数,需要注意结果的精度问题
function multiply(a, b) {
    return parseFloat((a*b).toFixed(10));
}

## 27.题目描述 将函数 fn 的执行上下文改为 obj,返回 fn 执行后的值
在JavaScript中,函数是一种对象,其上下文是可以变化的,对应的,函数内的this也是可以变化的,函数可以作为一个对象的方法,也可以同时作为另一个对象的方法,可以通过Function对象中的call或者apply方法来修改函数的上下文,函数中的this指针将被替换为call或者apply的第一个参数。将函数 fn 的执行上下文改为 obj 对象,只需要将obj作为call或者apply的第一个参数传入即可。

共有四种正确方案(按提交的运行时间由快到慢排列):

1. apply 方法改变函数 this 值(平均 172ms )

function alterContext(fn, obj) {
	return fn.apply(obj); 
}
2. call 方法改变函数 this 值(178 ms)

function alterContext(fn, obj) {
  return fn.call(obj); 
}
3. bind 方法创建指定 this 值的函数实例(208 ms)

function alterContext(fn, obj) {
  const bindedFn = fn.bind(obj);
  return bindedFn();
}
4. 给 obj 增加 fn 方法(213 ms)

function alterContext(fn, obj) {
    obj.fn = fn;
  return obj.fn();
}

## 28.给定一个构造函数 constructor,请完成 alterObjects 方法,将 constructor 的所有实例的 greeting 属性指向给定的 greeting 变量。
function alterObjects(constructor, greeting) {
     constructor.prototype.greeting = greeting;    
}

## 29.题目描述 找出对象 obj 不在原型链上的属性(注意这题测试例子的冒号后面也有一个空格~) 1、返回数组,格式为 key: value 2、结果数组不要求顺序
题目要求找不在原型链上的属性,即返回实例属性,有3种正确方法(按提交运行速度由快到慢排列):
1. Object.keys 方法(156 ms)
返回可枚举的实例属性的数组。

function iterate(obj) {
    return Object.keys(obj).map(function(key) {
        return key + ":  " + obj[key];
    });
}

2. for-in 和 hasOwnProperty 方法(171 ms)
前者用于遍历所有属性,后者用于判断是否为实例属性。

function iterate(obj) {
    const res = [];
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) {
            res.push(prop + ":  " + obj[prop]);
        }
    }
    return res;
}
3. Object.getOwnPropertyNames 方法(209 ms)
用法跟1一样,区别在于返回的是所有实例属性(包括不可枚举的)。

function iterate(obj) {
    return Object.getOwnPropertyNames(obj).map(function(key) {
        return key + ":  " + obj[key];
    });
}

注意:
1. 居然不支持 let。。。。。。改成 var 就行了
2. 是的,箭头函数也不支持。。。
3. 题目没有说明返回的实例属性是否包括不可枚举的,这里理解为包括。否则,由于 Object.keys() 返回的仅为可枚举的实例属性,应该无法通过。

## 30.题目描述 给定字符串 str,检查其是否包含数字,包含返回 true,否则返回 false
function containsNumber(str) {
    return /\d/.test(str)
}

## 31.题目描述 给定字符串 str,检查其是否包含连续重复的字母(a-zA-Z),包含返回 true,否则返回 false
function containsRepeatingLetter(str) {
    return /([a-zA-Z])\1/.test(str);   //\1表示重复正则第一个圆括号内匹配到的内容;  \2表示重复正则第二个圆括号内匹配到的内容
}

## 32.题目描述 获取数字 num 二进制形式第 bit 位的值。注意: 1、bit 从 1 开始  2、返回 0 或 1   3、举例:2 的二进制为 10,第 1 位为 0,第 2 位为 1
function valueAtBit(num, bit) {
    return (num >> (bit -1)) & 1;
}

## 33.题目描述 给定二进制字符串,将其换算成对应的十进制数字
function base10(str) {
    /**
        其它进制转十进制
        parseInt(str,2)
        parseInt(str,8)
        parseInt(str,16)
    */
    return parseInt(str,2);
}

## 34.题目描述 将给定数字转换成二进制字符串。如果字符串长度不足 8 位,则在前面补 0 到满8位。
首先通过toString方法将num转为2进制数形式,然后判断其长度是否足够8位。如不足8位,则声明一个“0000000”字符串用于补0,因为目标的2进制数形式最少为一位,因此最多只需要7个0;通过slice方法对“0000000”进行截取,然后将其结果加在目标前面即可。
unction convertToBinary(num){
			//转换为2进制格式
			var s = num.toString(2);
			//获得2进制长度
			var len = s.length
			if(len < 8){
				//声明一个字符串用于补满0
				var s1 = '00000000'
				var s2 = s1.slice(0,8-len)
				s=s2 + s
			}
			return s
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值