Web前端面试题----js篇

565 阅读9分钟

1、如何将一个多维数组变成一维数组?

方法一:转字符串法
    let tempArr = [1,[2,3],[4,5,[6,7]]];
    let result_str1 = tempArr.join(',').split(',');
    console.log(result_str1); //  ["1", "2", "3", "4", "5", "6", "7"]

该方法的缺点是如果数组中的各个项是number,将不可避免的转为字符串。

改进

使用了map进行再次遍历。 这种情况只适用于数组中各个项都是number类型或string类型的情况

 function unid(arr){
    let result_str1 = arr.join(',').split(',');
    let result_number = result_str1.map(item=>{
        return Number(item);
    });
    return result_number;
}
    console.log(unid(tempArr)); // [1, 2, 3, 4, 5, 6, 7]
方法二:使用数组的concat方法,以及apply

数组的concat方法   

  let tempArr2 = ['3',4],

  tempArr3 = ['3',4,[5,6]];

  console.log([1,2].concat(tempArr2)); // [1, 2, "3", 4]

  console.log([1,2,].concat(tempArr3)); // [1, 2, "3", 4, [5,6]]

所以使用concat方法只能把二维数组转为一维数组

    let tempArr4 = [[1,2],'3','4',[5,6],[7]];
    console.log([].concat.apply([],tempArr4)); // [1, 2, "3", "4", 5, 6, 7]
方法三:使用递归
```

使用递归来实现多维数组转为一维数组

let result = [],
    tempArr5 = ['1',2,[3,4,[5,6]],7];
function unid1(arr){
    for(let item of arr){
        if(Object.prototype.toString.call(item).slice(8, -1)==='Array'){
            unid1(item);
        }else{
            result.push(item);
        }
    }
    return result;
}
console.log(unid1(tempArr5)); // ["1", 2, 3, 4, 5, 6, 7]

2、去除数组中重复的方法?

1、使用filter函数来过滤多余的数组项
 var arr1 = [1, 2, 4, 5, 6, 7, 1, 2, 3, 4, 4, 3, 2];

function demo(arr){

    let arr1 = arr.filter(function (item,index,self){

        return index == self.indexOf(item);

    })

    console.log(arr1)

    return arr1;

}

demo(arr);

 

2、使用for循环来循环出多余的数组项
 arr = ['Ron', 'Pal', 'Fred', 'Rongo', 'Ron',123,'Fred'];

function demo(arr) {

    let demo1 = [];

    arr.forEach(function (i) {

        if(!demo1[i]) {

            demo1[i] = true;

        }

    });

    console.log(Object.keys(demo1))

    return Object.keys(demo1);

}

console.log(demo(arr));

3、Js中的循环方式有哪些?

1for循环,通常遍历数组;

2for...in循环,对数组或者对象的属性进行循环操作;

3while循环,通常用来循环数组;

4do while循环,它在循环开始前先执行一次操作,然后才进行判断,true就继续执行,false就结束循环;

5、forEach循环,用于调用数组的每个元素,并将元素传递给回调函数。对于空数组不会执行回调函数; 

6、map方法,返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值;

7for...of循环。

4、Js中的变量存储?

引用类型保存在堆中,如:object、array、function;

基础类型保存在栈中,如:undefinednullbooleannumberstring

5、Js中的判断是否为数组的方法有哪些?

1Array.isArray()返回布尔值truefalse2instanceof运算符   返回布尔值 truefalse;

3、通过constructor判断;arr.constructor  === Array 若为true则为数组类型

4、通过Object.prototype.toString.call()判断

6、js原型和js原型链?

在js中我们是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个prototype属性值,
这个属性值是一个对象,这个对象包含了可以由构造函数的所有实例共享的属性和方法。

当我们使用构造函数新建一个对象后,在这个对象的内部包含一个指针,
这个指针指向构造函数的prototype属性对应的值,在ES6中这个指针被称为对象的原型,
一般来说我们是不能够获取到这个值的,但是现在浏览器中都实现了_proto_属性让我们来访问这个属性,
但是我们最好不要使用这个属性,因为他不是规范中规定的。

ES6中新增了一个Object.getPrototypeOf() 方法,我们可以通过这个方法来获取对象的原型。
当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象上找这个属性,
这个原型对象上又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。


原型链的尽头一般来说都是Object.prototype,所以这就是我们新建对象为什么能够实用toString()等方法的原因。
特点:
JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。
当我们修改原型时,与之相关的对象也会继承这一改变。

7、什么是浅拷贝和深拷贝?

深浅拷贝.jpg

浅拷贝
就是两个变量都是指向一个地址,改变了一个变量,那另一个变量也随之改变。
这就是浅拷贝带来的副作用,两个变量会相互影响到,因为它们指向同一个地址。

NewArray = Array
深拷贝
就是互相独立,指向的是不同的地址,一个变量改变了,另一个变量不会被影响到。

newArray = _.map(array, (a) => { return a })闭包函数

8、什么是闭包函数?

定义: 当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的其它变量,
如果返回的这个函数在外部被执行,就产生了闭包。

表现形式:使函数外部能够调用函数内部定义的变量。
为什么要使用闭包?
使用闭包主要是为了设计私有的方法和变量。
闭包的优点是可以避免全局变量的污染,
缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
在js中,函数即闭包,只有函数才会产生作用域的概念
闭包函数的三个特性?
函数嵌套函数

函数内部可以引用外部的参数和变量

参数和变量不会被垃圾回收机制回收

9、cookie、sessionStorage、localStorage的区别?

cookie.png

sessionStorage 和 localStorageHTML5 storage API 提供的,
可以把数据保存在本地,避免了数据在浏览器和服务器之间不必要的通信。
sessionStorage,localStorage, cookie 都是在浏览器存储的数据。
不同之处:
1:cookie数据始终在同源http中携带,即使不需要,也会在浏览器和服务器间来回传递。Storage只存在本地;

2:cookie数据有路径概念,可以限制cookie只能在某路径下;

3:cookie:4k;Storage:5M;

4:sessionStorage:仅在当前浏览器关闭前有效;localStorage:始终有效;cookie:在过期前有效;

5:sessionStorage:在打开的不同浏览器窗口不共享,即使是同一页面,
   localStorage:在同源页面共享;cookie:同源页面共享;
   

10、JavaScript实现继承的方式有哪几种?

1、原型链的继承
核心: 将父类的实例作为子类的原型
特点:
1.  纯粹的继承关系,实例是子类的实例,也是父类的实例
2.  父类新增原型方法/原型属性,子类都能访问到
3.  简单,易于实现
缺点:
1.  可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在`new Animal()`这样的语句之后执行。
2.  无法实现多继承
3.  来自原型对象的所有属性被所有实例共享
4.  创建子类实例时,无法向父类构造函数传参
2、构造继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
特点:
1.  解决了1中,子类实例共享父类引用属性的问题
2.  创建子类实例时,可以向父类传递参数
3.  可以实现多继承(call多个父类对象)
缺点:
1.  实例并不是父类的实例,只是子类的实例
2.  只能继承父类的实例属性和方法,不能继承原型属性/方法
3.  无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
3、组合继承
核心: 通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
4、实例继承
核心: 为父类实例添加新特性,作为子类实例返回
特点:
1.  不限制调用方式,不管是`new 子类()`还是`子类()`,返回的对象具有相同的效果
缺点:
1.  实例是父类的实例,不是子类的实例
2.  不支持多继承
5、Class 继承
在 ES6 中,我们可以使用 `class` 去实现继承,并且实现起来很简单
核心: 使用 `extends` 表明继承自哪个父类,并且在子类构造函数中必须调用 `super`

11、在jQuery中怎么阻止事件冒泡?*

1.event.stopPropagation();
在事件处理过程中,阻止了事件冒泡,但是不会阻止默认行为(比如a标签的跳转,不会组织链接的跳转)
2、return false;
在事件处理过程中,既阻止事件冒泡,也阻止事件的默认行为
3.event.preventDefault();
在事件处理过程中,阻止了事件的默认行为,但是不会阻止事件的冒泡

12、在JavaScript中怎么向数组中添加或者删除数据?*

push()向数组的最后一项添加

pop()删除数组的最后一项

unshift()向数组的第一项添加

shift()删除数组的第一项

13、什么是DOM和BOM?

bom和dom.webp

DOM指的是文档对象模型,它指的是把文档当做一个对象来对待,这个对象主要定义了处理网页内容的方法和接口。

BOM指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的方法和接口。

BOM的核心是window,而window对象具有双重角色,它及时通过js访问浏览器窗口的一个接口,又是一个Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window对象都含有location对象、navigator对象、screen对象等子对象,并且DOM的最根本对象document也是BOM的window对象的子对象

BOM的最根本对象是window。DOM最根本对象是document(实际上是window.document)。

14、js中有什么数据类型?

数据类型.jpg

js中有六种数据类型,包括五种基本数据类型(Number,String,Boolean,Undefined,Null)
复杂数据类型(Object)。

15、关于js中的递归和正则匹配?

递归.jpg

递归求和
function sum(n) {
   if (n === 1) return 1
   return sum(n - 1) + n
 }
 console.log(sum(100))
斐波拉契数列
递归的写法
function fib(n) {
   if (n === 1 || n === 2) return n - 1
   return fib(n - 1) + fib(n - 2)
 }
 console.log(fib(10)) // 34
 
非递归的写法
 function demo(n){
   let a = 0;
   let b = 1;
   let c = a + b;

   for (let i = 3; i<n ; i++){
       a = b;
       b = c;
       c = a + b;
   }

   return c
 }
 console.log(demo(10))

16、关于自由落体运动和匀速圆周运动?

(基本不会考到/个别的公司的面试问题)

wenku.baidu.com/view/35a073…

17、js中事件执行的顺序

  • 1、事件捕获
  • 2、事件处理
  • 3、事件冒泡
  • 4、阻止事件冒泡