前端面试(代码)


JS数组去重

Array.prototype.unique1 = function () {

  var n = []; //一个新的临时数组

  for (var i = 0; i < this.length; i++) //遍历当前数组
  {

      //如果当前数组的第i已经保存进了临时数组,那么跳过,

      //否则把当前项push到临时数组里面

     if (n.indexOf(this[i]) == -1) 
       n.push(this[i]);

  }
  return n;
}
Array.prototype.unique2 = function()
{

   var n = {},r=[]; //n为hash表,r为临时数组

   for(var i = 0; i < this.length; i++) //遍历当前数组
    {

        if (!n[this[i]]) //如果hash表中没有当前项
        {

            n[this[i]] = true; //存入hash表

            r.push(this[i]); //把当前数组的当前项push到临时数组里面

        }

    }
    return r;
}
Array.prototype.unique3 = function()
{

    var n = [this[0]]; //结果数组

    for(var i = 1; i < this.length; i++) //从第二项开始遍历
    {

        //如果当前数组的第i项在当前数组中第一次出现的位置不是i,

        //那么表示第i项是重复的,忽略掉。否则存入结果数组

        if (this.indexOf(this[i]) == i) n.push(this[i]);

    }

    return n;

}
arr = [...new Set(arr)]; //ES6新方法


js中for循环中的setTimeout()正常工作
我们希望每隔2秒依次输出1,2,3

for (var i = 1; i <= 3; i++) {
    setTimeout(function () {
                console.log(i);
            },2000);
}

实际上却是,在间隔2秒后直接输出3个3。
原因在于计时器是共享一个i,当for循环结束时,i已经变成3,所以这时就会多次弹出3.所以通过定义一个函数来实现中介的作用,从而创建了变量的值的副本。

function doSetTimeout(i) {
  setTimeout(function() { console.log(i); }, 2000);
}

for (var i = 1; i <= 3; ++i)
  doSetTimeout(i);

这时虽然输出1,2,3,但却是间隔2秒后一次性输出,并未达到我们要求的每隔2秒输出一个要求。所以这时我们要调整setTimeout的时间间隔

function doSetTimeout(i) {
  setTimeout(function() { console.log(i); }, 2000*i);
}

for (var i = 1; i <= 3; ++i)
  doSetTimeout(i);

<SCRIPT LANGUAGE="JavaScript">
var bb = 1;
function aa(bb) {
    bb = 2;
    alert(bb);
};
aa(bb);
alert(bb);
SCRIPT>

结果:依次弹出 2和1
分析:函数体内,bb并没有使用var来定义,按理说这个bb在预处理的时候应该是window的属性。但在这里,函数声明的时候,带了一个参数bb,也就是相当于在函数体内声明了var bb。所以,函数里的bb就是函数活动对象的属性。所以函数执行时会输出2。函数执行完后,函数的活动对象被销毁,也就是局部的这个bb被删除了,执行流进入到window,再输出bb,值就是1了。如果声明函数时,把参数那里的bb去掉,这段代码执行起来,结果就是弹出 2 2


有一个长度未知的数组a,如果它的长度为0就把数字1添加到数组里面,否则按照先进先出的队列规则让第一个元素出队。

分析:这道题主要是考核了数组的队列方法和栈方法。

a.length === 0 ? a.push(1) : a.shift();

var test = (
   function(a) {
     this.a = a;
     return function(b) {         return this.a + b;     }
   }
   (function(a, b) {
        return a;  }(1, 2)
   )
); 
console.log(test(4)); 

输出5,考查自执行函数和闭包


请把

  • 第1行
  • 第2行
  • ...
(ul之间有10个li元素)插入body里面,注意:需要考虑到性能问题。
分析:这题主要考察了dom操作。插入节点操作的可以使用insertBefore和appendChild方法,随便用一个都行。但是,题目要求要考虑性能问题,这才是关键,因为,JavaScript操作dom的开销是很大的!提高性能就要减少dom操作。因此,我当时使用了下面的方法,只操作一次dom就够的了:

var lis = ""; 
var ul = document.createElement("ul");   //把li以字符串形式生成
for(var i = 1; i <= 10; i++) {     
   lis += "
  • 第" + i + "行
  • "
    ; } // 最后通过innerHTML插入ul里面 ul.innerHTML = lis; //这里才操作dom,把ul插入到body document.body.appendChild(ul);


    不使用loop循环,创建一个长度为100的数组,并且每个元素的值等于它的下标。
    (1)使用setInterval

    var a = [],     i = 0;
    var interval = setInterval(function() {
                      i < 100 ? a.push(i++) : clearInterval(interval);
                    }, 0);

    (2)使用数组

    var a = Array(100).join(",").split(",").map(
      function(item, index) {
       return index;
      }
    );
    先是创建一个数组,然后,通过join方法把它转成字符串,然后,再通过split方法把字符串转成数组,这时候,它就拥有100个值为空的元素了,然后,再通过map函数,改变这些元素的值即可。

    (3)es6方法

    var arrNew = Array.from({length: 100},(v,k)=>k);
    console.log(arrNew );
    var arr = new Array(100).keys();
    console.log(Array.from(arr));

    错误方法:

    var a = new Array(100);
    a = a.map(function(item, index) {     return index; });
    
    因为JavaScript数组是稀疏数组,比如,通过new Array(100)创建一个新的数组的,虽然他的长度是100,但是实际上他是一个空数组,也就是说没有真实存在的元素。所以使用map方法,根本不会去遍历这个数组100次的。


    实现数组乱序

    var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],     sign = 1;  a.sort(function(a, b) {
         //因为Math.random产生的数在0-1之间
        //所以0.5两边的概率是相等的
        //大于0.5时为升序,小于0.5时为降序
        sign = (Math.random() > 0.5) ? 1 : -1;
        return (a - b) * sign;   });


    有一个长度为100的数组,请以优雅的方式求出该数组的前10个元素之和
    分析:其实,对于数组求和有很多种方法,也很简单。但是,这题有两个限制条件:优雅的方式、前10个元素。对于“前10个元素”这个限制条件可以使用Array.prototype.slice()方法来截取,对于”优雅的方式”,我的理解是应该尽可能使用数组自带的方法,最好可以使用高阶函数!所以我觉得应该是Array.prototype.reduce()方法。

    var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], sum = 0;
    sum = a.slice(0, 10).reduce(function(pre, current)  {
                                     return pre + current; });   console.log(sum); //55

    注意, Array的reduce()把一个函数作用在这个Array的[x1, x2, x3…]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算

    var User = {     count: 1,
                     getCount: function() {     return this.count;   }
                }; 
    console.log(User.getCount());  //输出1
    var func = User.getCount;   
    console.log(func());   //输出Undefined

    这里func的上下文是window,因此已经失去了count属性。改进如下,使用Function.prototype.bind

    var func = User.getCount.bind(User);
    console.log(func());   

    十一
    解析URL地址
    这里的方法就在JS代码里先创建一个a标签然后将需要解析的URL赋值给a的href属性

    function parseURL(url) {
       var a =  document.createElement('a');
       a.href = url;
       return {
           source: url,
           protocol: a.protocol.replace(':',''),
           host: a.hostname,
           port: a.port,
           query: a.search,
           params: (function(){
               var ret = {},
                   seg = a.search.replace(/^\?/,'').split('&'),
                   len = seg.length, i = 0, s;
               for (;iif (!seg[i])
                    { continue; }
                   s = seg[i].split('=');
                   ret[s[0]] = s[1];
               }
               return ret;
           })(),
           file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
           hash: a.hash.replace('#',''),
           path: a.pathname.replace(/^([^\/])/,'/$1'),
           relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
           segments: a.pathname.replace(/^\//,'').split('/')
       };
    }

    用法

    
    var myURL = parseURL('http://abc.com:8080/dir/index.html?id=255&m=hello#top');
    
    myURL.file;     // = 'index.html'
    myURL.hash;     // = 'top'
    myURL.host;     // = 'abc.com'
    myURL.query;    // = '?id=255&m=hello'
    myURL.params;   // = Object = { id: 255, m: hello }
    myURL.path;     // = '/dir/index.html'
    myURL.segments; // = Array = ['dir', 'index.html']
    myURL.port;     // = '8080'
    myURL.protocol; // = 'http'
    myURL.source;   // = 'http://abc.com:8080/dir/index.html?id=255&m=hello#top'

    十二
    重构如下两栏布局
    前端面试(代码)_第1张图片

    <div class="content"><div class="aside">div><div class="main">div>div>
    /*Qzone版*/
    .aside{float:left;width:170px;}
    .main{float:left;width:790px;}
    /*朋友网版*/
    .aside{float:left;width:170px;}
    .main{overflow:hidden;#zoom:1;}
    /*Facebook版*/
    .aside{float:left;width:170px;}
    .main{margin-left:170px;zoom:1;}
    /*Yahoo版*/
    .content{position:relative;zoom:1;}
    .aside{position:absolute;left:0;top:0;width:170px;}
    .main{margin-left:170px;zoom:1;}
    /*Google版*/
    .aside{display:inline-block;width:170px;}
    .main{display:inline-block;width:790px;}

    Qzone、朋友网、Facebook都给左栏浮动,唯一不同的是右栏的写法,Qzone给右栏定宽并且浮动,而朋友网和Facebook则并没有给右栏定宽也未浮动,而是利用了创建BFC并且为低版本IE触发hasLayout的原理让右栏自适应宽度。
    Yahoo和Google两栏都未用浮动,唯一不同的是Yahoo用了绝对定位的方法,而谷歌用了inline-block,Google已经宣布旗下一些产品放弃对IE8 的支持,所以Google可以大胆的使用inline-block去实现布局,不用去为其他低版本浏览器写一大堆的hack。

    十三
    常见邮箱验证
    输入的数据必须包含 @ 符号和点号(.)。同时,@ 不可以是邮件地址的首字符,并且 @ 之后需有至少一个点号

    function validateForm(){
      var x=document.forms["myForm"]["email"].value;
      var atpos=x.indexOf("@");
      var dotpos=x.lastIndexOf(".");
      if (atpos<1 || dotpos2 || dotpos+2>=x.length){
        alert("不是一个有效的 e-mail 地址");
        return false;
      }
    }







    ?????????讲一下css的动画(我不太了解, 就直说不太了解,然后她让我试着写了下,就写了些印象里的,她说差不多)
    猜一下,如果要绑定动画的话,在哪里绑定?怎么绑定?
    如果是行内元素呢?如果字体font-size是10px,行高line-height是2px,显示多高?(依然不知道,猜测是2px,剩余的部分会被隐藏掉)。刚才试了下,发现打错了:

    十四
    找出整型数组中乘积最大的三个数

    var unsorted_array = [-10, 7, 29, 30, 5, -10, -70];
    
    computeProduct(unsorted_array); // 21000
    
    function sortIntegers(a, b) {
      return a - b;
    }
    
    // greatest product is either (min1 * min2 * max1 || max1 * max2 * max3)
    function computeProduct(unsorted) {
      var sorted_array = unsorted.sort(sortIntegers),
        product1 = 1,
        product2 = 1,
        array_n_element = sorted_array.length - 1;
    
      // Get the product of three largest integers in sorted array
      for (var x = array_n_element; x > array_n_element - 3; x--) {
          product1 = product1 * sorted_array[x];
      }
      product2 = sorted_array[0] * sorted_array[1] * sorted_array[array_n_element];
    
      if (product1 > product2) return product1;
    
      return product2
    };

    十五
    给定两个数组,要求求出两个数组的交集,注意,交集中的元素应该是唯一的。

    var firstArray = [2, 2, 4, 1];
    var secondArray = [1, 2, 0, 2];
    
    intersection(firstArray, secondArray); // [2, 1]
    
    function intersection(firstArray, secondArray) {
      var hashmap = {};
      var intersectionArray = [];
    
      firstArray.forEach(function(element) {
        hashmap[element] = 1;
      });
    
      secondArray.forEach(function(element) {
        if (hashmap[element] === 1) {
          intersectionArray.push(element);
          hashmap[element]++;
        }
      });
    
      return intersectionArray;
    }

    十六
    给定两个字符串,判断是否颠倒字母而成的字符串,譬如Mary与Army就是同字母而顺序颠倒:

    var firstWord = "Mary";
    var secondWord = "Army";
    
    isAnagram(firstWord, secondWord); // true
    
    function isAnagram(first, second) {
      // For case insensitivity, change both words to lowercase.
      var a = first.toLowerCase();
      var b = second.toLowerCase();
    
      // Sort the strings, and join the resulting array to a string. Compare the results
      a = a.split("").sort().join("");
      b = b.split("").sort().join("");
    
      return a === b;
    }

    十七
    判断某个字符串是否为回文字符串,譬如racecar与race car都是回文字符串:

    function isPalindrome(word) {
      // Replace all non-letter chars with "" and change to lowercase
      var lettersOnly = word.toLowerCase().replace(/\s/g, "");
    
      // Compare the string with the reversed version of the string
      return lettersOnly === lettersOnly.split("").reverse().join("");
    }

    十八
    判断是否为 2 的指数值

    // For the non-zero case:
    function isPowerOfTwo(number) {
      return number & (number - 1) === 0;
    }
    
    // For zero-case:
    function isPowerOfTwoZeroCase(number) {
      return (number !== 0) && ((number & (number - 1)) === 0);
    }

    十九
    二进制转换

    function decimalToBinary(digit) {
      if(digit >= 1) {
        if (digit % 2) {
          return decimalToBinary((digit - 1) / 2) + 1;
        } else {
          // Recursively return proceeding binary digits
          return decimalToBinary(digit / 2) + 0;
        }
      } else {
        // Exit condition
        return '';
      }
    }

    二十
    创建一个函数来判断给定的表达式中的大括号是否闭合:

    var expression = "{{}}{}{}"
    var expressionFalse = "{}{{}";
    
    isBalanced(expression); // true
    isBalanced(expressionFalse); // false
    isBalanced(""); // true
    
    function isBalanced(expression) {
      var checkString = expression;
      var stack = [];
    
      // If empty, parentheses are technically balanced
      if (checkString.length <= 0) return true;
    
      for (var i = 0; i < checkString.length; i++) {
        if(checkString[i] === '{') {
          stack.push(checkString[i]);
        } else if (checkString[i] === '}') {
          // Pop on an empty array is undefined
          if (stack.length > 0) {
            stack.pop();
          } else {
            return false;
          }
        }
      }
    
      // If the array is not empty, it is not balanced
      if (stack.pop()) return false;
      return true;
    }

    二十一
    二分搜索
    (1)首先,从有序数组的中间的元素开始搜索,如果该元素正好是目标元素(即要查找的元素),则搜索过程结束,否则进行下一步。
    (2)如果目标元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半区域查找,然后重复第一步的操作。
    (3)如果某一步数组为空,则表示找不到目标元素。

         // 非递归算法
            function binary_search(arr, key) {
                var low = 0,
                    high = arr.length - 1;
                while(low <= high){
                    var mid = parseInt((high + low) / 2);
                    if(key == arr[mid]){
                        return  mid;
                    }else if(key > arr[mid]){
                        low = mid + 1;
                    }else{
                        high = mid -1;
                    }
                }
                return -1;
            };
            var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
            var result = binary_search(arr,10);
            alert(result); // 9 返回目标元素的索引值       
        // 递归算法
            function binary_search(arr,low, high, key) {
                if (low > high){
                    return -1;
                }
                var mid = parseInt((high + low) / 2);
                if(arr[mid] == key){
                    return mid;
                }else if (arr[mid] > key){
                    high = mid - 1;
                    return binary_search(arr, low, high, key);
                }else if (arr[mid] < key){
                    low = mid + 1;
                    return binary_search(arr, low, high, key);
                }
            };
            var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
            var result = binary_search(arr, 0, 13, 10);
            alert(result); // 9 返回目标元素的索引值  

    二十二
    手写数组快速排序
    “快速排序”的思想很简单,整个排序过程只需要三步:
    (1)在数据集之中,选择一个元素作为”基准”(pivot)。
    (2)所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。
    (3)对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

     var quickSort = function(arr) {
      if (arr.length <= 1) { return arr; }
      var pivotIndex = Math.floor(arr.length / 2);
      var pivot = arr.splice(pivotIndex, 1)[0];
      //这里注意splice返回,是一个包含被删除项目的新数组,所以执行后arr已经删除元素,而【0】则代表被删除的pivot
      var left = [];
      var right = [];
      for (var i = 0; i < arr.length; i++){
        if (arr[i] < pivot) {
          left.push(arr[i]);
        } else {
          right.push(arr[i]);
        }
      }
      return quickSort(left).concat([pivot], quickSort(right));
    };

    你可能感兴趣的:(前端面试)