前端面试汇总-js篇

98 阅读12分钟

### 下面这些值哪些是假值?( A

0;
new Number(0);
("");
(" ");
new Boolean(false);
undefined;
  • A: 0 "" undefined
  • B: 0 new Number(0) "" new Boolean(false) undefined
  • C: 0 "" new Boolean(false) undefined
  • D: 所有都是假值。

分析:

JavaScript 中假值只有 6 个:false""nullundefinedNaN0

1. ajax、axios、fetch 的区别

ajax 是指一种创建交互式网页应用的网页开发技术,并且可以做到无需重新加载整个网页的情况下,能够更新部分网页,也叫作局部更新。

使用 ajax 发送请求是依靠于一个对象,叫 XmlHttpRequest 对象,通过这个对象我们可以从服务器获取到数据,然后再渲染到我们的页面上。现在几乎所有的浏览器都有这个对象,只有 IE7 以下的没有,而是通过 ActiveXObject 这个对象来创建的。

Fetchajax 非常好的一个替代品,基于 Promise 设计,使用 Fetch 来获取数据时,会返回给我们一个 Pormise 对象,但是 Fetch 是一个低层次的 API,想要很好的使用 Fetch,需要做一些封装处理。

下面是 Fetch 的一些缺点

  • Fetch 只对网络请求报错,对 400,500 都当做成功的请求,需要封装去处理
  • Fetch 默认不会带 cookie,需要添加配置项。
  • Fetch 不支持 abort,不支持超时控制,使用 setTimeoutPromise.reject 的实现超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费。
  • Fetch 没有办法原生监测请求的进度,而 XHR 可以。

Vue2.0 之后,axios 开始受到更多的欢迎了。其实 axios 也是对原生 XHR 的一种封装,不过是 Promise 实现版本。它可以用于浏览器和 nodejsHTTP 客户端,符合最新的 ES 规范。

关于promiss状态

image.png

关于promiss优缺点

优点:能解决回调函数异步处理时带来的回调地狱问题(回调函数嵌套); 缺点:代码冗余,可读性差、不易理解

event loop

执行栈从任务队列中读取异步任务的回调函数,放到执行栈中依次执行,这个过程是循环不断的,所以叫事件循环;
时间循环过程: image.png

宏任务与微任务

宏任务:

  1. ajax请求;
  2. setTimeout、setIntevel;
  3. 文件操作;

微任务:

  1. promiss.then(),.catch,.finaly();
  2. process.nextTickt

微宏任务执行顺序: 每一个宏任务执行完之后,都会检查待执行的微任务,如果有微任务,则会在执行完微任务之后,在执行下一个宏任务; image.png

前端工程化开发

image.png

vue

**特点:**数据驱动视图,双向绑定

mvvm:
model: 渲染页面所需要的数据; view: 页面的dom结构 viewModel: vue实例

监听器与计算属性

**watch:**主要用于侦听数据的变化 属性: handler(){}
deep (深度监听)
immediate (立即处理 进入页面就触发)

**computed:**是一个动态计算出来的属性值。

对变量进行类型判断的方式有哪些

常用的方法有 4 种:

  1. typeof

typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 7 种:number、boolean、symbol、string、object、undefined、function 等。

  1. instanceof

instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型。

  1. constructor

当一个函数 F 被定义时,JS 引擎会为 F 添加 prototype 原型,然后再在 prototype 上添加一个 constructor 属性,并让其指向 F 的引用。

  1. toString

toString( )  是 Object 的原型方法,调用该方法,默认返回当前对象的 Class 。这是一个内部属性,其格式为  [object Xxx]  ,其中 Xxx 就是对象的类型。

对于 Object 对象,直接调用 toString( )  就能返回  [object Object]  。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。例如:

Object.prototype.toString.call('') ;  // [object String]
Object.prototype.toString.call(1) ;   // [object Number]
Object.prototype.toString.call(true) ;// [object Boolean]
Object.prototype.toString.call(Symbol());//[object Symbol]
Object.prototype.toString.call(undefined) ;// [object Undefined]
Object.prototype.toString.call(null) ;// [object Null]

强制类型转换方法有哪些?

参考答案:

JavaScript 中的数据类型转换,主要有三种方式:

  1. 转换函数

js 提供了诸如 parseInt 和 parseFloat 这些转换函数,通过这些转换函数可以进行数据类型的转换 。

  1. 强制类型转换

还可使用强制类型转换(type casting)处理转换值的类型。

例如:

  • Boolean(value) 把给定的值转换成 Boolean 型;

  • Number(value)——把给定的值转换成数字(可以是整数或浮点数);

  • String(value)——把给定的值转换成字符串。

  1. 利用 js 变量弱类型转换。

例如:

  • 转换字符串:直接和一个空字符串拼接,例如:a = "" + 数据
  • 转换布尔:!!数据类型,例如:!!"Hello"
  • 转换数值:数据*1 或 /1,例如:"Hello * 1"

说一下 JS 中类型转换的规则?

参考答案:

类型转换可以分为两种,隐性转换显性转换

1. 隐性转换

当不同数据类型之间进行相互运算,或者当对非布尔类型的数据求布尔值的时候,会发生隐性转换。

预期为数字的时候:算术运算的时候,我们的结果和运算的数都是数字,数据会转换为数字来进行计算。

类型转换前转换后
number44
string"1"1
string"abc"NaN
string""0
booleantrue1
booleanfalse0
undefinedundefinedNaN
nullnull0

预期为字符串的时候:如果有一个操作数为字符串时,使用+符号做相加运算时,会自动转换为字符串。

预期为布尔的时候:前面在介绍布尔类型时所提到的 9 个值会转为 false,其余转为 true

2. 显性转换

所谓显性转换,就是只程序员强制将一种类型转换为另外一种类型。显性转换往往会使用到一些转换方法。常见的转换方法如下:

  • 转换为数值类型:Number()parseInt()parseFloat()
  • 转换为布尔类型:Boolean()
  • 转换为字符串类型:toString()String()

当然,除了使用上面的转换方法,我们也可以通过一些快捷方式来进行数据类型的显性转换,如下:

  • 转换字符串:直接和一个空字符串拼接,例如:a = "" + 数据
  • 转换布尔:!!数据类型,例如:!!"Hello"
  • 转换数值:数据*1 或 /1,例如:"Hello * 1"

常见的内存泄露,以及解决方案

参考答案:

内存泄露概念

内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。

内存泄漏通常情况下只能由获得程序源代码和程序员才能分析出来。然而,有不少人习惯于把任何不需要的内存使用的增加描述为内存泄漏,即使严格意义上来说这是不准确的。

JS 垃圾收集机制

JS 具有自动回收垃圾的机制,即执行环境会负责管理程序执行中使用的内存。在C和C++等其他语言中,开发者的需要手动跟踪管理内存的使用情况。在编写 JS 代码的时候,开发人员不用再关心内存使用的问题,所需内存的分配 以及无用的回收完全实现了自动管理。

Js中最常用的垃圾收集方式是标记清除(mark-and-sweep)。当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占的内存,因为只要执行流进入相应的环境,就可能用到它们。而当变量离开环境时,这将其 标记为“离开环境”。

常见内存泄漏以及解决方案

  1. 意外的全局变量

Js处理未定义变量的方式比较宽松:未定义的变量会在全局对象创建一个新变量。在浏览器中,全局对象是window。

ini
复制代码
function foo(arg) { 
    bar = "this is a hidden global variable"; //等同于window.bar="this is a hidden global variable"
    this.bar2= "potential accidental global";//这里的this 指向了全局对象(window),等同于window.bar2="potential accidental global"
}

解决方法:在 JavaScript 程序中添加,开启严格模式'use strict',可以有效地避免上述问题。

注意:那些用来临时存储大量数据的全局变量,确保在处理完这些数据后将其设置为null或重新赋值。与全局变量相关的增加内存消耗的一个主因是缓存。缓存数据是为了重用,缓存必须有一个大小上限才有用。高内存消耗导致缓存突破上限,因为缓 存内容无法被回收。

  1. 循环引用

在js的内存管理环境中,对象 A 如果有访问对象 B 的权限,叫做对象 A 引用对象 B。引用计数的策略是将“对象是否不再需要”简化成“对象有没有其他对象引用到它”,如果没有对象引用这个对象,那么这个对象将会被回收 。

ini
复制代码
let obj1 = { a: 1 }; // 一个对象(称之为 A)被创建,赋值给 obj1,A 的引用个数为 1   
let obj2 = obj1; // A 的引用个数变为 2  

obj1 = 0; // A 的引用个数变为 1  
obj2 = 0; // A 的引用个数变为 0,此时对象 A 就可以被垃圾回收了

但是引用计数有个最大的问题: 循环引用。

ini
复制代码
function func() {  
    let obj1 = {};  
    let obj2 = {};  

    obj1.a = obj2; // obj1 引用 obj2  
    obj2.a = obj1; // obj2 引用 obj1  

}

当函数 func 执行结束后,返回值为 undefined,所以整个函数以及内部的变量都应该被回收,但根据引用计数方法,obj1 和 obj2 的引用次数都不为 0,所以他们不会被回收。要解决循环引用的问题,最好是在不使用它们的时候手工将它们设为空。上面的例子可以这么做:

ini
复制代码
obj1 = null;  
obj2 = null;
  1. 被遗忘的计时器和回调函数
ini
复制代码
let someResource = getData();  
setInterval(() => {  
    const node = document.getElementById('Node');  
    if(node) {  
        node.innerhtml = JSON.stringify(someResource));  
    }  
}, 1000);

上面的例子中,我们每隔一秒就将得到的数据放入到文档节点中去。

但在 setInterval 没有结束前,回调函数里的变量以及回调函数本身都无法被回收。那什么才叫结束呢?

就是调用了 clearInterval。如果回调函数内没有做什么事情,并且也没有被 clear 掉的话,就会造成内存泄漏。

不仅如此,如果回调函数没有被回收,那么回调函数内依赖的变量也没法被回收。上面的例子中,someResource 就没法被回收。同样的,setTiemout 也会有同样的问题。所以,当不需要 interval 或者 timeout 时,最好调用 clearInterval 或者 clearTimeout

  1. DOM 泄漏

在 JS 中对DOM操作是非常耗时的。因为JavaScript/ECMAScript引擎独立于渲染引擎,而DOM是位于渲染引擎,相互访问需要消耗一定的资源。 而 IE 的 DOM 回收机制便是采用引用计数的,以下主要针对 IE 而言的。

a. 没有清理的 DOM 元素引用

ini
复制代码
var refA = document.getElementById('refA');
document.body.removeChild(refA);
// refA 不能回收,因为存在变量 refA 对它的引用。将其对 refA 引用释放,但还是无法回收 refA。

解决办法:refA = null;

b. 给 DOM 对象添加的属性是一个对象的引用

ini
复制代码
var MyObject = {}; 
document.getElementById('mydiv').myProp = MyObject;

解决方法:
在 window.onunload 事件中写上: document.getElementById('mydiv').myProp = null;

c. DOM 对象与 JS 对象相互引用

ini
复制代码
function Encapsulator(element) { 
	this.elementReference = element; 
	element.myProp = this; 
} 
new Encapsulator(document.getElementById('myDiv'));

解决方法: 在 onunload 事件中写上: document.getElementById('myDiv').myProp = null;

d. 给 DOM 对象用 attachEvent 绑定事件

csharp
复制代码
function doClick() {} 
element.attachEvent("onclick", doClick);

解决方法: 在onunload事件中写上: element.detachEvent('onclick', doClick);

e. 从外到内执行 appendChild。这时即使调用 removeChild 也无法释放

ini
复制代码
var parentDiv = document.createElement("div"); 
var childDiv = document.createElement("div"); 
document.body.appendChild(parentDiv); 
parentDiv.appendChild(childDiv);

解决方法: 从内到外执行 appendChild:

ini
复制代码
var parentDiv = document.createElement("div"); 
var childDiv = document.createElement("div"); 
parentDiv.appendChild(childDiv); 
document.body.appendChild(parentDiv);
  1. JS 的闭包

闭包在 IE6 下会造成内存泄漏,但是现在已经无须考虑了。值得注意的是闭包本身不会造成内存泄漏,但闭包过多很容易导致内存泄漏。闭包会造成对象引用的生命周期脱离当前函数的上下文,如果闭包如果使用不当,可以导致环形引用(circular reference),类似于死锁,只能避免,无法发生之后解决,即使有垃圾回收也还是会内存泄露。

  1. console

控制台日志记录对总体内存配置文件的影响可能是许多开发人员都未想到的极其重大的问题。记录错误的对象可以将大量数据保留在内存中。注意,这也适用于:

(1) 在用户键入 JavaScript 时,在控制台中的一个交互式会话期间记录的对象。
(2) 由 console.log 和 console.dir 方法记录的对象。

193. 设计⼀个⽅法(isPalindrom)以判断是否回⽂(颠倒后的字符串和原来的字符串⼀样为回⽂)

参考答案:

示例代码如下:

javascript
复制代码
function isPalindrome(str) {
    if (typeof str !== 'string') {
        return false
    }
    return str.split('').reverse().join('') === str
}

// 测试
console.log(isPalindrome('HelleH')); // true
console.log(isPalindrome('Hello')); // false

194. 设计⼀个⽅法(findMaxDuplicateChar)以统计字符串中出现最多次数的字符

function findMaxDuplicateChar(str) {
    let cnt = {},	//用来记录所有的字符的出现频次
        c = '';		//用来记录最大频次的字符
    for (let i = 0; i < str.length; i++) {
        let ci = str[i];
        if (!cnt[ci]) {
            cnt[ci] = 1;
        } else {
            cnt[ci]++;
        }
        if (c == '' || cnt[ci] > cnt[c]) {
            c = ci;
        }
    }
    console.log(cnt); // { H: 1, e: 1, l: 3, o: 2, ' ': 1, W: 1, r: 1, d: 1 }
    return c;
}

// 测试
console.log(findMaxDuplicateChar('Hello World')); // l

vite与vuecli

image.png