乐趣区

关于面试:前端常见面试题总结JavaScript部分二

1. 如何对一个数组进行去重 / 排序?

去重

除了罕用的双重循环,还有两种办法

办法一:遍历该数组,利用 indexOf()办法判断新数组中是否存在,不存在就 push 到新驻足中,代码如下:



1.  var arr = ['a', 'b', 'b', 'c', 'c', 'd'];
    
2.  var newArr = [];
    
3.  for (var i = 0; i < arr.length; i++) {4.      if (newArr.indexOf(arr[i]) == -1) {5.          newArr.push(arr[i]);
    
6.      }
    
7.  }
    

办法二:通过 es6 中的 set 数据结构,对数组去重



1.  var setNum = new Set(arr);
    
2.  Array.from(setNum);
    

4.  // 或者用 rest 参数
    
5.  [...setNum]
    

 排序

办法一:利用数组的 sort()办法排序,如果调用该办法时没有应用参数,是依照字符编码的程序进行排序,并不是数字大小排序。



1.  var arr = [5, 1, 3, 6, 8, 12];
    
2.  arr.sort();    //[1, 12, 3, 5, 6, 8]
    

4.  arr.sort(function (a, b) {
    
5.      return a - b;
    
6.  });    
    
7.  console.log(arr);    //[1, 3, 5, 6, 8, 12]
    

 办法二:双重循环,冒泡排序



1.  var arr2 = [5, 1, 3, 6, 8, 2, 14, 17];
    
2.  for(var i = 0; i < arr2.length - 1; i++) {3.      for(var j = 0; j < arr2.length - 1 - i; j++) {4.          if (arr2[j] > arr2[j + 1]) {5.              var item = arr2[j];
    
6.              arr2[j] = arr2[j + 1];
    
7.              arr2[j + 1] = item;
    
8.          }
    
9.      }
    
10.  }
    
11.  console.log(arr2);    //[1, 2, 3, 5, 6, 8, 14, 17]
    

 还有数组排序的其余几种算法,能够参考:https://www.cnblogs.com/real-me/p/7103375.html

2. 什么是简略类型(原始类型 primitive type),什么是简单类型(合成类型 complex type 或援用类型)。

原始类型就是间接存储在栈中的数据,是最根本的数据类型,不能够再分了。

  • 数值(number):整数和小数(比方 13.14
  • 字符串(string):文本(比方Hello World)。
  • 布尔值(boolean):示意真伪的两个非凡值,即true(真)和false(假)
  • undefined:示意“未定义”或不存在,即因为目前没有定义,所以此处临时没有任何值
  • null:示意空值,即此处的值为空。
  • Symbol:示意举世无双的,是 es6 中新增的类型

简单类型就是指存储在堆中的数据。罕用简单类型如下:

  • 数组(Array): 存储一系列的值
  • 日期(Date):用于解决日期和工夫
  • 算数(Math):执行一般的算数工作
  • 正则表达式(RegExp): 形容了字符的模式对象   

3. 深拷贝与浅拷贝的区别,如何进行深拷贝?

深拷贝和浅拷贝都是针对简单类型的,简略类型没有深浅拷贝之分。

对于仅仅是复制了援用(地址),换句话说,复制了之后,原来的变量和新的变量指向同一个货色,彼此之间的操作会相互影响,为 浅拷贝

而如果是在堆中从新分配内存,领有不同的地址,然而值是一样的,复制后的对象与原来的对象是齐全隔离,互不影响,为 深拷贝

深浅拷贝 的次要区别就是:复制的是援用(地址) 还是复制的是实例。

那么如何进行深拷贝呢?

办法一:利用 jQuery 中的 extend()办法实现深拷贝

_$_.extend([deep], target, object1 [, objectN] )

deep:示意是否深度合并对象,为 true 是就是神拷贝

target Object 类型 指标对象,其余对象的成员属性将被附加到该对象上。

object1  objectN可选。Object 类型 第一个以及第 N 个被合并的对象。



1.  var obj = {name:'ming',age:19,company : { name : 'RNG', address : '北京'} };
    
2.  var obj_extend = $.extend(true,{}, obj); 
    
3.  console.log(obj === obj_extend);    //false
    

extend()办法能够参考菜鸟教程:http://www.runoob.com/jquery/misc-extend.html

办法二:利用 JSON 对象的 parse 和 stringify 办法



1.  var obj = {name:'xixi',age:20,company : { name : '腾讯', address : '深圳'} };
    
2.  var obj_json = JSON.parse(JSON.stringify(obj));
    
3.  console.log(obj === obj_json);    //false
    

办法三:es6 中的 rest 参数

var aa = [...array];   //es6

办法四:利用 递归 来实现深复制,对属性中所有援用类型的值,遍历到是根本类型的值为止。



1.  function deepClone(source){2.    if(!source && typeof source !== 'object'){3.      throw new Error('error arguments', 'shallowClone');    
    
4.    }    
    
5.    var targetObj = Array.isArray(source) ? [] : {};    
    
6.    for(var keys in source){7.      if(source.hasOwnProperty(keys)){8.        if(source[keys] && typeof source[keys] === 'object'){9.          targetObj[keys] = deepClone(source[keys]);    // 递归 
    
10.        }else{11.          targetObj[keys] = source[keys];         
    
12.        }       
    
13.      }    
    
14.    }    
    
15.    return targetObj; 
    
16.  }
    

留神 :Array 对象的 slice() 和 concat()办法不是真正的深拷贝。

参考自知乎:https://zhuanlan.zhihu.com/p/26282765(这里具体写了深浅拷贝的原理,此处不再赘述)

4. 说几个罕用的数组 / 字符串的原生办法

数组:

  • splice() 办法可删除从 index 处开始的零个或多个元素,并且用参数列表中申明的一个或多个值来替换那些被删除的元素。如果从 arrayObject 中删除了元素,则返回的是含有被删除的元素的数组。
  • reverse() 办法用于颠倒数组中元素的程序。该办法会扭转原有数组。
  • slice()办法 返回一个新的数组,蕴含从 start 到 end(不包含该元素)的 arrayObject 中的元素。
  • join() 办法用于把数组中的所有元素放入一个字符串。元素是通过指定的分隔符进行分隔的。

字符串:

  • indexOf() 办法可返回某个指定的字符串值在字符串中首次呈现的地位。没有则返回 -1。
  • split() 办法用于把一个字符串宰割成字符串数组。
  • slice() 办法可提取字符串的某个局部,并以新的字符串返回被提取的局部。
  • substring() 办法用于提取字符串中介于两个指定下标之间的字符。

5. 面向对象的继承有几种形式?

形式一:原型链继承
批改子类的 prototype 为父类,这样会使子类的原型对象的 constructor 变成了父类,也能够手动批改回来
子类.prototype=new 父类 ();
子类.prototype.constructor= 子类;
特点:
(1)十分纯正的继承关系,实例是子类的实例,也是父类的实例
(2) 继承父类自身的属性 / 办法、父类原型属性 / 办法、父类新增原型办法 / 原型属性
(3) 无奈实现多继承
(4) 创立子类实例时,无奈向父类构造函数传参
(5) 子类的一个实例属性值扭转时,会影响所有子类的实例属性。因为所有子类的 prototype 指向父类(new 父类),所有没有设置本人的属性的子类的实例属性都会扭转。
然而如果是子类的一个实例属性从新赋值(子类的实例设置了本人的属性),则不会影响其余实例的属性。



1.  function A(){
    
2.      this.aNum=1;
    
3.  }
    
4.  A.prototype.getaNum= function () {5.      console.log(this.aNum);
    
6.  }
    

8.  B.prototype=new A();
    
9.  var b=new B();
    
10.  console.log(b.aNum);    // 父类自身的属性和办法
    
11.  b.getaNum();    // 原型链上的属性和办法
    

 形式二:构造函数继承(对象假冒)

子类调用父类的构造函数,并且把 this 传进去,
子类函数中:父类.call(this);
子类函数中:父类.apply(this);
特点:
(1)只继承父类构造函数中的属性和办法, 不能继承原型属性 / 办法。
(2)能够实现多重继承(call/apply 多个父类对象)
(3)实例只是子类的实例,不是父类的实例。
(4)创立子类实例时,能够向父类传递参数
(5) 无奈实现函数复用,每个子类都有父类实例函数的正本,影响性能



1.  function E(num){
    
2.      this.eNum = num;
    
3.      this.geteNum = function () {4.          alert(this.eNum);
    
5.      }
    
6.  }
    

8.  function F(num){9.      E.call(this, num);
    
10.  }
    

12.  var f=new F(5);
    
13.  f.geteNum();
    

 形式三:组合式继承

通过调用父类结构,继承父类的属性并保留传参的长处,而后通过将父类实例作为子类原型,实现函数复用
特点:
(1)既能够继承父类自身的属性 / 办法,也能够继承原型属性 / 办法
(2) 既是子类的实例,也是父类的实例
(3) 不存在子类的一个实例属性值扭转时,会影响所有子类的实例属性的问题。
每创立一个子类对象实例,就调用父类的构造函数并且将实例对象作为 this 值传过来,所以每个实例对象都有本人的属性值。
(4)可传参
(5) 函数可复用
(6) 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)



1.  function G(){
    
2.      this.gNum = 6;
    
3.  }
    
4.  G.prototype.getgNum = function () {5.      console.log(this.gNum);
    
6.  }
    

8.  function H(){9.      G.call(this);
    
10.  }
    
11.  H.prototype = new G();
    

13.  var h = new H();
    
14.  console.log(h.gNum);
    
15.  h.getgNum();
    

6. 面向对象中的 this 代表什么,可否举例说明?

this 是一个指针,this 总是指向调用办法的对象,作为办法调用,那么 this 就是指实例化的对象。

举例说明:jQuery 中的链式调用,就是 this 对象的利用。

*7.说一下 事件委派(事件委托)的原理*

通事件委派的原理是事件冒泡机制,通过给父标签绑定事件,而后利用事件冒泡的景象使得点击子元素的时候,能够触发事件达到给子元素绑定事件的成果。

Event 对象提供了一个属性叫 target,示意为以后的事件操作的 dom,这个属性是有兼容性的,规范浏览器用 event.target,IE 浏览器用 event.srcElement。

长处:1. 能够大量节俭内存占用,缩小事件注册。比方 ul 上代理所有 li 的 click 事件。

           2. 能够实现当新增子对象时,无需再对其进行事件绑定,对于动静内容局部尤为适合。

8.window 的 ready 与 onload 事件的区别,执行的先后顺序是什么?**

ready,示意文档构造(DOM 构造)曾经加载实现(不蕴含图片等非文字媒体文件),
onload,批示页面蕴含图片等文件在内的所有元素都加载实现。

能够说:ready 在 onload 前加载!!!

9. 如何判断一个数据是不是数组?

通过 typeof 检测数组,失去的构造是 object,并不能辨别是不是数组,咱们能够通过以下几种办法来判断一个数据是不是数组。

办法一:instanceof  用来判断一个对象是否存在于另一个对象的原型链上。



1.  var str=["aa", "bb", "cc"];
    
2.  console.log(str instanceof Array);    //true
    

办法二:isArray 函数



1.  var arr=["aa", "bb", "cc"];
    
2.  console.log(Array.isArray(arr));    //true
    

办法三:constructor  属性返回对创立此对象的函数的援用, 应用此属性能够检测数组类型。



1.  var arr=["aa", "bb", "cc"];
    
2.  if(arr.constructor===Array){3.      console.log('array');
    
4.  }
    
5.  //array
    

退出移动版