前端面试题-JavaScript篇

web前端面试题 JavaScript篇

1、JavaScript的数据类型都有哪些(8条)?

ES5中有6种:Number、String、Boolean、Undefined、Null、Object
ES6新增了Symbol
谷歌6版本bigInt:是指安全存储、操作大整数

2、JS中基本类型和引用类型的区别

基本数据类型:
1.基本类型的变量是存放在栈区的
2.基本类型的比较是值的比较
3.基本类型的赋值在赋值操作后,两个变量是相互不受影响的
引用数据类型
1.引用类型在栈内存中保存的实际上是对象在堆内存中的引用地址
2.引用类型的比较是引用地址的比较
3.引用类型的赋值其实是保存在栈区引用地址的赋值,因此两个变量指向同一个对象,任何的操作都会相互影响

3、ES6的新特性有哪些?

1.变量声明:新增了let和const
2.模板字符串
3.箭头函数
4.函数的参数默认值
5.解构赋值
6.for…of 和 for…in
7.引入了class关键字。
8.模块import,export(ES6中的类)
9.Promise

4、var,let,const的区别

  1. var
    ① var定义的变量,没有块的概念,可以跨块访问。
    ②var定义的变量可以修改,如果不初始化会输出undefined,不会报错。
    ③重复声明不报错,视作修改。
  2. let
    ①定义的变量,只能在块作用域里访问,不能跨块访问,但不影响作用域链。
    ②不能重复声明变量。
  3. const
    ①const 定义的变量,一旦定义后,就不能修改,即 const 声明的为常量。使用时必须初始化(即必须赋值),只能在块作用域里访问。
    ②const 声明创建一个值的只读引用,只是变量标识符不能重新分配。所以,对象属性修改和数组元素变化不会出发 const 错误。
    ③声明对象类型一般使用 const,来规避误操作修改对象

回答模板
首先,var,let,const都是用来声明变量的关键字,他们之间的区别有:
1.var定义变量具备声明提升
2.var 声明变量可以重复声明,而 let 不可以重复声明
3.let和const必须先定义后使用,不能重复定义,拥有块级作用域,暂存性死区
4.const声明之后必须赋初始值,否则会报错,习惯上变量名大写,并且const定义的变量是只读变量,不能进行修改

5、数组去重的方法

方法一:遍历比较

var arr = [5, 5, 7, 7, 4, 9, 3, 1, 3];
    var arr1 = [];
    for (var i = 0; i < arr.length; i++) {
    
    
        if (arr1.indexOf(arr[i]) == -1) {
    
    
            arr1.push(arr[i]);
        }
    }
    console.log(arr1);

方法二:set

var arr = [5, 5, 7, 7, 4, 9, 3, 1, 3];
var newarr = [... new Set(arr)]
console.log(newarr);

方法三:

var arr = [5, 5, 7, 7, 4, 9, 3, 1, 3];
var newarr = Array.from( new Set(arr))
console.log(newarr);

6、js中判断数据类型的几种实用方法

typeof

typeof它返回结果只有以下几种:number,string,boolean,object,undfined,function
typeof在判断null时,返回值为object
判断应用类型时返回值为object,不能细致的具体到是哪一种 object。所以要 想区分对象、数组、null,单纯使用 typeof 是不行的。

instanceof:instanceof 用于判断某个对象是否被另一个函数构造

Instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof方法要求开发者明确地确认对象为某特定类型

var simpleStr = "This is a simple string"; 
var myString = new String();
var newStr = new String("String created with constructor");
var myDate = new Date();
var myObj = {
    
    };
var myNonObj = Object.create(null);

// 返回 false, simpleStr 并不是对象
simpleStr instanceof String;
// 返回 true
myString instanceof String;
// 返回 true
newStr instanceof String;
// 返回 true
myString instanceof Object;
// 返回 true
myObj instanceof Object;
// 返回 true
({
    
    }) instanceof Object;
// 返回 false, 一种创建非 Object 实例的对象的方法
myNonObj instanceof Object;
// 返回 false
myString instanceof Date;
// 返回 true
myDate instanceof Date;
// 返回 true
myDate instanceof Object;
// 返回 false
myDate instanceof String; 

Object.prototype.toString.call()

    Object.prototype.toString.call(null);// ”[object Null]”
    Object.prototype.toString.call(undefined);// ”[object Undefined]”
    Object.prototype.toString.call(“abc”);// ”[object String]”
    Object.prototype.toString.call(123);// ”[object Number]”
    Object.prototype.toString.call(true);// ”[object Boolean]”

7、为什么要用箭头函数?解决了什么问题?

箭头函数是匿名函数,ES5匿名函数的语法糖,并且没有自己的this,arguments
它的优点是:
(1)简洁的语法、
(2)隐式返回,如 下面的代码可以去掉return,代码移到一行,减少代码量
(3)解决了this的指向问题,原生的写法this指向的是调用者,箭头函数this绑定的是定义时的那个对象。如果有对象嵌套的情况,则this绑定到最近的一层对象上

8、箭头函数和普通函数的区别

箭头函数this不是指向 window,而是父级(指向是可变的)
箭头函数不能用作构造函数,因为它没有自己的 this,无法实例化。
因为箭头函数没有自己的 this,所以箭头函数内也不存在 arguments 对象。
可以通过bind修改this指向,普通函数通过apply,call来修改this指向

9、Call 和 apply,bind 的区别

call、apply、bind的作用是改变this的指向,也就是函数执行时的上下文
apply 接收两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组形式传入。改变this指向后原函数会立即执行,且方法只是临时改变this指向一次。当第一个参数是Null、undefined时,默认指向window
fn1.apply(想要将this指向哪里, [函数实参1, 函数实参2]);
call 与apply用法很像,不同的是从第二个参数是一个参数列表不是数组
fn1.call(想要将this指向哪里, 函数实参1, 函数实参2);
bind方法创建一个新的函数,当这个新的函数被调用时,其 this 置为提供的值,其参数列表前几项,置为创建时指定的参数序列

10、this指向问题

首先,this是函数的内置对象
1.普通函数执行,内部this默认是window,严格模式下是undefined
2.对象方法()执行,内部this是对象本身.
3.构造函数用new执行,内部this指向新创建的实例对象
4.箭头函数,箭头函数没有自己的this ,箭头函数中的this是箭头函数创建时当前上下文中的this。如果当前.上下文没有this就会继续向上寻找.
5.事件处理函数:函数中的this是当前元素本身
6.函数内部this被call,apply, bind强制修改,以修改后指向为准.
总结:箭头函数是创建时就已经确定了this,又由于箭头函数本身没有this,所以使用all等方法也无法改变其this指向,第一个参数将被忽略.普通函数则是执行时才明确this。

11、对闭包的理解?

概念:函数嵌套函数,被嵌套的函数被称为闭包函数
作用:在一个函数体外,使用了函数的局部变量,是将函数内部和函数外部连接起来的桥梁。
实现:在主函数fun中定义内部变量(count)以及函数(funzi),在子函数funzi中使用主函数的内部变量(count),将子函数(funzi)作为fun的返回值,在外界通过全局变量(f)与(fun)的返回值绑定,从而延长了(funzi)的生命周期,使得(count)可以在外界使用
缺陷:打破了垃圾回收机制,延长了局部变量的生命周期,使用闭包有可能会造成内存泄漏
应用:闭包的应用:实现变量的私有化,事件防抖,事件节流,偏函数与柯里化,bind等等函数

12、localStorage,sessionStorage,cookie的区别,你都用它们存储过什么?

区别 cookie localStorage sessionStorage
生命周期 设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭 除非主动删除数据,否则数据永远不会消失,即使窗口或浏览器关闭 仅在当前会话下有效
存储大小 4kb 5M 5M
数据与服务器之间的交互方式 cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端 不会自动把数据发给服务器,仅在本地保存 不会自动把数据发给服务器,仅在本地保存
易用性 源生的cookie接口不友好 ,需要程序员自己封装 源生接口可以接受亦可再次封装来对Object和Array有更好的支持 源生接口可以接受亦可再次封装来对Object和Array有更好的支持

13、怎么封装cookie的方法

 // 存储Cookie
    setCookie(key, value) {
    
    
        var day = 1;  // 缓存时间
        var path = '/'; // 设置路径,在同一路径下的网页可以共享cookie,路径不同时,不可以访问
        var date = new Date();
        date.setDate(date.getDate() + day); //设置失效时间
        document.cookie = encodeURIComponent(key) + '=' + encodeURIComponent(value) + ';expires=' + date + ';path=' + path;
    },
 // 获取Cookie
    getCookie(key) {
    
    
        var cookieStr = document.cookie;
        var cookieArr = cookieStr.split("; ");
        for (var i = 0; i < cookieArr.length; i++) {
    
    
            var temp = cookieArr[i].split("=");
            if (temp[0] === key) {
    
    
                return decodeURIComponent(temp[1]);
            }
        }
    },
// 删除Cookie
    delCookie(key) {
    
    
        var path = '/'
        CookieUtils.setCookie(key, 1, -1, path);
    },

14、浏览器端event loop工作流程(原理)是什么?

1.js中有同步任务、异步任务两种.
2.同步任务在JS引擎线程执行,形成执行栈
3.执行栈中的同步任务在执行过程中遇到异步任务,异步任务被放入任务队列,等待执行
4.执行栈中的任务运行完成后(JS引擎空闲),从任务队列中读取异步任务,加入到执行栈,并执行
5.主线程不断重复上面的第三步

15、任务队列,同步任务,异步任务?

任务队列主要分为两种:宏任务,微任务.
当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行.

同步任务:在主线程上,排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务

异步任务:不进入主线程,而进入“任务队列”的任务,只有“任务队列” 通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

15、节流防抖

防抖函数:是将多次执行变为最后一次执行,比如百度搜索

function debounce(func,time){
    
    
	var timerid = null;
	return function(){
    
    
		if(timerid){
    
    
			clearTimeout(timerid );
			timerid = null;
		}
		timerid = setTimeout(()=>{
    
    
			func();
		},time)
	}
}

节流函数 :是将多次执行变成每隔一段时间执行调用一次函数,而不是一触发事件就调用一次,这样就会减少资源浪费。

function throttle(func,time){
    
    
	var timerid = null;//保存定时器id
	return function(){
    
    
		if(!timerid){
    
    //定时器若不存在,则创建定时器
			timerid = setTimeout(()=>{
    
    
				func();
				clearTimeout(timerid );
				timerid = null;
			},time)
		}
	}
}

16、for in和for of的区别,以及for in遍历数组的打印值

for of 和 for in都是用来遍历的属性
1、推荐在循环对象属性的时候使用 for…in,在遍历数组的时候的时候使用 for…of
2、二者都可以循环数组,for in 输出的是数组的index下标,而for of 输出的是数组的每一项的值。
3、for in 可以遍历对象,for of 不能遍历对象

17、map和forEatch的区别

forEach 和 map 的相同点

  1. 都是循环遍历数组中的每一项
  2. forEach 和 map 方法里每次执行匿名函数都支持 3 个参数,参数分别是 item(当前每一项),
    index(索引值),arr(原数组)

forEach 和 map 的不同点

  1. map有返回值,返回值是由map回调函数决定
  2. forEach()会修改原来的数组。而map()方法会得到一个新的数组并返回。
  3. forEach() 的执行速度 < map() 的执行速度

18、数组(array)的方法有哪些

会改变数组自身的方法: push, pop, shift, unshift, splice ,sort, reverse,
不会改变数组自身的方法: concat, join, slice, toString
ES5数组方法: forEach,map, every, some, filter, reduce, indexOf, lastIndexOf
ES6数组扩展: from, of, find, findIndex, includes

19、字符串的方法有哪些

选取字符串: substr,substring ,slice
查询字符串: indexOf,includes, search,match
其他:split,replace ,charAt,toLowercase ,toUppercase ,trim

20、前端的定时器都有哪些,区别?

setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。
setInterval() 方法可按照指定的周期(以毫秒计)来调用函数或计算表达式。
区别
setInterval() 方法会不停地调用函数,直到 clearInterval() 被调用或窗口被关闭。由 setInterval() 返
回的 ID 值可用作 clearInterval() 方法的参数

21、Get和post的区别?

1.携带数据的方式: get使用url或cookie传参。而post将数据放在BODY中。
2.携带数据量: get的urI会有 长度上的限制,则post的数据则可以非常大。
3.数据安全性: post比get相对安全,因为数据在地址栏上不可见。

22、http 与https的区别

1、HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协

2、HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者
是 443
3、HTTP 的连接很简单,是无状态的。HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密
传输、身份认证的网络协议,比 HTTP 协议安全。

23、原型链,原型,构造函数、实例对象、原型对象三者之间的区别

原型:在 JavaScript 中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript
的对象中都包含了一个” prototype”内部属性,这个属性所对应的就是该对象的原型,原型的主要作用就是为了实现继承与扩展对象
原型链:当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去
prototype 里找这个属性,这个 prototype 又会有自己的 prototype,于是就这样一直找下去,
也就是我们平时所说的原型链的概念
构造函数:请描述一下构造函数,实例对象原型对象三者之间的关系?
每个构造函数都可以通过prototype指向原型对象,原型对象通过constructor指向构造函数,实例对象通过__ proto__ 指向原型对象

24、JavaScript中实现继承有哪些方案

1.原型链继承:通过原型对象实现继承
子类的原型对象=父类的实例化对象
缺陷

  1. 无法在子类对象构造时,初始化父类派生给子类的属性
  2. 必须先实现继承,再对子类对象添加新的属性和方法,一旦实现继承,原型对象的指向不能发生改变。
  function Parent1() {
    
    
    this.name = 'parent1';
    this.play = [1, 2, 3]
  }
  function Child1() {
    
    
    this.type = 'child2';
  }
  Child1.prototype = new Parent1();
  console.log(new Child1());

2.借用构造函数继承
缺陷:原型对象的属性无法完成继承

  function Parent1(){
    
    
    this.name = 'parent1';
  }
  Parent1.prototype.getName = function () {
    
    
    return this.name;
  }
  function Child1(){
    
    
    Parent1.call(this);
    this.type = 'child1'
  }
  let child = new Child1();
  console.log(child);  // 没问题
  console.log(child.getName());  // 会报错

3.混合继承:通过原型继承和借用构造方法继承,实现完善的继承

  function Parent3 () {
    
    
    this.name = 'parent3';
    this.play = [1, 2, 3];
  }
  Parent3.prototype.getName = function () {
    
    
    return this.name;
  }
  function Child3() {
    
    
    // 第二次调用 Parent3()
    Parent3.call(this);
    this.type = 'child3';
  }
  // 第一次调用 Parent3()
  Child3.prototype = new Parent3();
  // 手动挂上构造器,指向自己的构造函数
  Child3.prototype.constructor = Child3;
  var s3 = new Child3();
  var s4 = new Child3();
  s3.play.push(4);
  console.log(s3.play, s4.play);  // 不互相影响
  console.log(s3.getName()); // 正常输出'parent3'
  console.log(s4.getName()); // 正常输出'parent3'

4.Es6继承: ES6中的继承就是通过类来实现的,通过class定义父类和子类,子类通过extends继承父类,通过super关键字访问和调用对象在父类上的方法(可以调用父类的构造方法,也可以调用父类的普通方法)

//定义父类
class Parent{
    
    
    constructor(name,gender){
    
    
        this.name = name;
        this.gender = gender;
    };
    get(){
    
    
        console.log("姓名:",this.name);
        console.log("性别:",this.gender);
    }
}
class Child extends Parent{
    
    
    constructor(name,gender,age){
    
    
        super(name,gender);  //super必须在子类的this之前调用
        this.age = age;
    }
    disp(){
    
    
        console.log("年龄:",this.age)
    }
    change(){
    
    
        console.log(this.name+'的性别是'+this.gender,'今年'+this.age)
    }
}
var c1 = new Child('小红','女',20);
c1.get();  //姓名: 小红  性别: 女
c1.disp(); //年龄: 20
c1.change(); //小红的性别是女 今年20

26、对象的遍历方法

for in:可以遍历数字,返回的是对应的key

for (let i in obj){
    
    
  执行的代码块
}

Object.keys()、Object.values()、Object.entries()
Object.keys():返回包含对象键名的数组
Object.values():返回包含对象键值的数组
Object.entries():返回包含对象键名和键值的数组

let obj={
    
    
 id:1,
 name:'hello',
 age:18
}
console.log(Object.keys(obj)) //['id','name','age']
console.log(Object.values()) //[1,'hello',18]
console.log(Object.entries()) //[['id',1],['name','hello'],['age',18]]

27、跨域如何解决

1.设置document domain读取非同源网页的Cookie
2 postMessage(),跨文档通信API.
3.JSONP,服务器与客户端跨源通信.
4.CORS ,跨域资源共享,服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的.
5.webpack本地代理,在webpack. config .js中配置本地代理proxy
6.websocket,实现了浏览器与服务器的全双工通信
7.Nginx反向代理,需 要搭建一个中转 nginx服务器,用于转发请求

28、深拷贝和浅拷贝,实现方式

方法一:使用for in遍历,加递归

let obj ={
    
    name:"hhh",age:18};
let newobj = {
    
    }
function fun(obj){
    
    
	for(let key in obj){
    
    
		if(obj.ket.typeof=="object"){
    
    
			fun(obj.ket)
		}else{
    
    
			newobj.key=obj.key
		}
	}
}

方法二:JSON.parse()和JSON.stringify()

JSON.parse(JSON.stringify(obj))

29、阻止浏览器默认事件,事件冒泡

阻止事件冒泡的方法:event.stopPropagation();
阻止默认行为: e.preventDefault()

30、什么是内存泄露?

内存泄漏指任何对象在您不再拥有或需要它之后仍然存在
闭包,定时器等等都会造成闭包

31、什么是Promise?我们用Promise来解决什么问题

1、Promise是异步编程的一种解决方案
2、promise有 三种状态: pending(等待态),resolve(成功态),rejected(失败态);
3、状态一旦改变,就不会再变。
4、创造promise实例后,它会立即执行Promise用来解决传统异步回调出现的回调地狱问题.
5、promise构造函数是同步执行的,then方法是异步执行的

32、setTimeout、Promise、Async/Await 的区别

事件循环中分为宏任务队列和微任务队列
宏任务(macrotask):在新标准中叫 task
主要包括:script(整体代码),setTimeout,setInterval,setImmediate,I/O,ui rendering
微任务(microtask):在新标准中叫 jobs
主要包括:process.nextTick, Promise,MutationObserver(html5 新特性)

setTimeout、Promise、Async/Await 的区别
setTimeout 的回调函数放到宏任务队列里,等到执行栈清空以后执行
Promise.then 里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行
async 函数表示函数里面可能会有异步方法,await 后面跟一个表达式
async 方法执行时,遇到 await 会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行

33、ES6,ES5类的创建?

ES6创建类:es6中所有的属性和方法都定义在constructor中

class Point{
    
    
	constructor(x,y,z) {
    
    
	    this.x = x;
	    this.y = y;
	    this.z = z;
	}
	toString(){
    
    
		return '(' + this.x + ',' + this.y + ',' + this.z + ')';
	}
}
let p = new Point(89,56,12);
console.log(p,typeof p,typeof Point,Point === Point.prototype.constructor);

ES5创建类

function Point(x,y,z){
    
    
	this.x = x;
	this.y = y;
	this.z = z;
}
Point.prototype.toString = function(){
    
    
	return '(' + this.x + ',' + this.y + ',' + this.z + ')';
}
var p = new Point(23,67,98);
console.log(p,typeof p,typeof Point,Point === Point.prototype.constructor);

34、promise打印数据和setTimeout回调数据,哪个先执行

先微后宏,所以先promise,后setTimeout

35、算法题:输入一个字符串,判断每个字母出现的次数

var str="aakjaahgaaayjaaabbbrfghe";
    function unique(str) {
    
    
   var obj={
    
    };
    for(var i=0;i<str.length;i++){
    
    
        if(obj[str[i]]){
    
    
            obj[key]++;
        }else{
    
    
            obj[key]=1;//obj[w]=1
        }
    }
   var arr = Object.keys(obj);   
   for(var i=0;i<arr.length;i++){
    
    
      console.log(arr[i]+'出现次数为:'+obj[arr[i]])
   }
 }
 unique(str)

持续更行中。。。

猜你喜欢

转载自blog.csdn.net/qq_45822157/article/details/129291664