💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
### 2. js中的基础数据类型有哪几种? 了解包装对象吗? 答:六种,string, number, boolean, undefiend, null, symbol 基础数据类型临时创建的临时对象,称为包装对象。其中 number、boolean 和 string 有包装对象,代码运行的过程中会找到对应的包装对象,然后包装对象把属性和方法给了基本类型,然后包装对象被系统进行销毁。 ## 3.对内存泄漏的了解 1. 理解 - 定义:程序中已在堆中分配的内存,因为某种原因未释放或者无法释放的问题 - 简单理解: 无用的内存还在占用,得不到释放和归还,比较严重的时候,无用的内存还会增加,从而导致整个系统卡顿,甚至崩溃。 2. 生命周期 1. 分配期   分配所需要的内存,在js中,是自动分配的 2. 使用期   使用分配的内存,就是读写变量或者对象的属性值 3. 释放期   不需要时将该内存释放,js会自动释放(除了闭包和一些bug以外)   内存泄漏就是出现在这个时期,内存没有被释放导致的 3. 可能出现内存泄漏的原因 1. 意外的全局变量 2. DOM元素清空时,还存在引用 3. 闭包 4. 遗忘的定时器 **如何优化内存泄漏?** * 全局变量先声明在使用 * 避免过多使用闭包。 * 注意清除定时器和事件监听器。 ## 4.js中数组合并的方法 js 数组合并 let arr1 = \['温情', '刘聪'\] let arr2 = \['杨和苏', '邓紫棋'\] let arr3 = \['周延'\] **1\. arr1.concat(arr2, ······)** es5 Array.concat() 合并两个数组, 返回新数组,不会改变原数组 arr = arr1.concat(arr2, arr3); console.log(arr);  // \["温情", "刘聪", "杨和苏", "邓紫棋", "周延"\] **2\. \[…arr1, …arr2,······\]** es6 展开运算符(…) arr = \[...arr1, ...arr2, ...arr3\]; console.log(arr);  // \["温情", "刘聪", "杨和苏", "邓紫棋", "周延"\] **3\. push(…arr)** push 结合 ...\[\] 来实现, 会更改原数组 arr1.push(...arr2, ...arr3) console.log(arr1);  // \["温情", "刘聪", "杨和苏", "邓紫棋", "周延" **适合两个数组,不适合多个数组的方法** **1\. for + push** for(let i in arr2) {     arr1.push(arr2\[i\]) } console.log(arr1);  // \["温情", "刘聪", "杨和苏", "邓紫棋"\] **2\. arr1.push.apply(arr1, arr2)** arr1.push.apply(arr1, arr2) console.log(arr1);  // \["温情", "刘聪", "杨和苏", "邓紫棋"\] ## 5.合并对象的方法 **Object.assign()** es6 Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target) Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。 let obj1 = {name: '温情'} let obj2 = {age: '22'} **const newObj = Object.assign({}, obj1, obj2);** console.log(newObj);  // {name: "温情", age: "22"} !注意! Object.assign()实行的是浅拷贝,也就是说如果源对象的属性是一个对象,那么目标对象得到的是这个对象的引用 let obj1 = {name: {chinese: '杨和苏', english: 'keyNG'}} const newObj = Object.assign({}, obj1); console.log(newObj);  // name: {chinese: "杨和苏", english: "keyNG"} obj1.name.english = 'pig'; console.log(newObj);  // name: {chinese: "杨和苏", english: "pig"} ## 6.什么是作用域,什么是作用域链? * 规定变量和函数的可使用范围称为作用域 * 查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作用域链。 ## **7.JS如何实现异步编程(5种)?** 1) 回调函数(callback) 优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。) 缺点:回调地狱,每个任务只能指定一个回调函数,不能 return. 2) 事件监听。这种思路是说异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。比如一个我们注册一个按钮的点击事件或者注册一个自定义事件,然后通过点击或者trigger的方式触发这个事件。 3) Promise 4) Generator 5) 生成器 async/await,是ES7提供的一种解决方案。 ## 8.js中的堆内存与栈内存 在js引擎中对变量的存储主要有两种位置,**堆内存和栈内存**。 和java中对[内存](https://so.csdn.net/so/search?q=%E5%86%85%E5%AD%98&spm=1001.2101.3001.7020 "内存")的处理类似,**栈内存**主要用于存储各种**基本类型的**变量,包括Boolean、Number、String、Undefined、Null,\*\*以及对象变量的指针,这时候栈内存给人的感觉就像一个线性排列的空间,每个小单元大小基本相等。 而堆内存主要负责像对象Object这种变量类型的存储,如下图 ![](https://img.kancloud.cn/38/a9/38a9d4738573d51c84d5a044539c1bcc_674x482.png) **栈内存中的变量一般都是已知大小或者有范围上限的**,算作一种简单存储。**而堆内存存储的对象类型数据对于大小这方面,一般都是未知的**。个人认为,这也是为什么null作为一个object类型的变量却存储在栈内存中的原因。 **因此当我们定义一个const对象的时候,我们说的常量其实是指针,就是const对象对应的堆内存指向是不变的,但是堆内存中的数据本身的大小或者属性是可变的。而对于const定义的基础变量而言,这个值就相当于const对象的指针,是不可变。** 既然知道了const在内存中的存储,那么const、let定义的变量不能二次定义的流程也就比较容易猜出来了,每次使用const或者let去初始化一个变量的时候,会首先遍历当前的内存栈,看看有没有重名变量,有的话就返回错误。 说到这里,有一个十分很容易忽略的点,之前也是自己一直没有注意的就是,使用new关键字初始化的之后是不存储在栈内存中的。为什么呢?new大家都知道,根据构造函数生成新实例,这个时候生成的是**对象**,而不是基本类型。再看一个例子 ![](https://img.kancloud.cn/af/06/af06d389390afe9d73f861ab7de4a136_652x168.png)  我们可以看到new一个String,出来的是对象,而直接字面量赋值和工厂模式出来的都是字符串。但是根据我们上面的分析大小相对固定可预期的即便是对象也可以存储在栈内存的,比如null,为啥这个不是呢?再继续看 ![](https://img.kancloud.cn/00/01/00018db3ae15d55daf8f682dcd9de9a9_293x88.png) 很明显,如果a,b是存储在栈内存中的话,两者应该是明显相等的,就像null === null是true一样,但结果两者并不相等,说明两者都是存储在堆内存中的,指针指向不一致。 ## 9.如何去判断js数据类型? **         首先我们可以用typeof去判断,typeof只能判断基本数据类型,对于引用数据类型,- -律返回object,在js中,数组是一种特殊的对象类型, 因此typeof-个数组,返回的是object.         还可以通过instanceof来判断,它不能检测基本数据类型,它是用来判断个实例是否属于某种类型, 使用它的方式可以用Ainstanceof B,如果A是B的实例,则返回true,否则返回flase。         然后还可以用constructor来判断,除了undefined和nul1之外,其它类型都可以通过constructor来判断,         但是如果声明了一个构造函数,并且把它的原型指向改变了,这种情况下,constructor也不能准确的判断。         通过0bject . prototype . toString,判断一个对象 只属于某种内置类型,但是不能准确的判断一个实例是否属于某种类型。 原因是因为实例对象可能会自定义toString方法,把这个方法给覆盖掉,我们可以通过函数. call( )方法,可以在任意值上调用这个方法,帮助我们判断这个值的类型。** ## 10,怎么允许跨域(跨域解决办法) **A、JSONP** 在页面上,js脚本,css样式文件,图片这三种资源是可以与页面本身不同源的。jsonp就利用了script标签进行跨域取得数据。 JSONP允许用户传递一个callback参数给服务器端,然后服务器端返回数据时会将这个callback参数作为函数名来包裹住JSON数据。这样客户端就可以随意定制自己的函数来自动处理返回的数据了。 JSONP只能解决get请求,不能解决post请求。 ``` <script> function callback(data){ console.log(data); } </script> <script src="http://localhost:80/?callback=callback"></script> ``` 使用ajax实现跨域: ``` <script src="http://code.jquery.com/jquery-latest.js"></script> $.ajax({ url:'http://localhost:80/?callback=callback', method:'get', dataType:'jsonp', //=> 执行jsonp请求 success:(res) => { console.log(res); } }) function callback(data){ console.log(data); } ``` **B、 CORS跨域资源共享:** 浏览器会自动进行CORS通信,实现CORS通信的关键是后端。服务端设置Access-Control-Allow-Origin就可以开启CORS。该属性表示哪些域名跨域访问资源。 主要设置以下几个属性: Access-Control-Allow-Origin//允许跨域的域名 Access-Control-Allow-Headers//允许的header类型 Access-Control-Allow-Methods//跨域允许的请求方式 ### C、Nginx反向代理 通过nginx配置一个代理服务器将客户机请求转发给内部网络上的目标服务器;并将服务器上返回的结果返回给客户端。 ### D、webpack(在vue.config.js文件中)中 配置webpack-dev-server ``` devServer: { proxy: { '/api': { target: "http://39.98.123.211", changeOrigin: true, //是否跨域 }, }, }, ``` ## 11.怎么让对象的一个属性不可被改变 ### (1) Object.defineProperty() 可以使用Object.defineProperty()方法,让对象的某一个属性不可变,把对象某一属性的writable和configurable设置为false. ``` let obj = {a:1,b:2}; Object.defineProperty(obj,'c',{ value:100000, writable:false,//当该属性的 writable 键值为 true 时,属性的值才能被赋值操作修改 configurable:false//当为true时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 }) obj.c = 282031283 console.log(obj.c)//100000 ``` ### 2)object.preventExtensions() 让对象不能添加新属性,可以使用object.preventExtensions()方法。(但是可以修改属性值) ``` let obj = {a:1,b:2}; Object.preventExtensions(obj); obj.c = 1000; console.log(obj) ``` ## 12.浏览器所用的内核 * IE:Trident内核 * Chrome:以前是webkit内核,现在是Blink内核 * Firefox:Gecko(/ˈɡekoʊ/)内核 * Safari:webkit内核 * Opera:最初使用的是presto内核,后来加入谷歌大军,从webkit内核又变成了Blink内核 * 360,猎豹浏览器:IE+chrome双内核 ## 13、判断一个函数是普通函数还是构造函数(补全funcA(){}) > 构造函数中this指向new创建的实例。所以可通过在函数内部判断this是否为当前函数的实例进而判断当前函数是否作为构造函数。 ``` function A(){ if(this instanceof A){ console.log('我是构造函数') }else{ console.log('我是普通函数') } } A(); new A(); ``` ## **14.JavaScript 中的提升是什么?** 提升意味着所有的声明都被移动到作用域的顶部。这发生在代码运行之前。 对于函数,这意味着你可以从作用域中的任何位置调用它们,甚至在它们被定义之前。 ``` hello(); // Prints "Hello world! " even though the function is called "before" declaration function hello(){ console.log("Hello world! "); } ``` 对于变量,提升有点不同。它在作用域的顶部将 undefined 分配给它们。 例如,在定义变量之前调用它: ``` console.log(dog);//undefined var dog = "Spot"; ``` ## **15、js有哪些内置对象?**  数据封装类对象:Object、Array、Boolean、Number 和 String   其他对象:Function、Arguments、Math、Date、RegExp、Error.... ## 16.防抖和节流 ### **函数防抖** 当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定时间到来之前,又触发了事件,就重新开始延时。也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于既定时间,那么防抖的情况下只会执行一次。 ### 函数节流 当持续触发事件时,保证在一定时间内只调用一次事件处理函数,意思就是说,假设一个用户一直触发这个函数,且每次触发小于既定值,函数节流会每隔这个时间调用一次 用一句话总结防抖和节流的区别:防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行 总结防抖和节流函数: js代码区别就在于if这里是清除定时器还是直接return终止函数 ~~~javascript function throttleOrDebounce(fn,delay=3000) { let timer=0; return function (){ // 防抖 // if(timer) clearTimeout(timer); // 节流 if (timer) return; timer=setTimeout(()=>{ fn.apply(this,arguments) timer=0 },delay) } } ~~~ ## 17.对象的深拷贝  深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,而是就连值也都复制了。  深拷贝之后,两个对象将毫无关联,不会相互影响。 ## 18、 Nodelist和HTMLCollection的区别 1.Nodelist是返回节点的集合,而nodelist里面也有数组,因此元素也是节点的一种,也就是元素节点**nodelist包含所有的节点 :注释节点、text节点、element节点.....等** ## 22.for和foreach谁更快,为什么? 使用性能测试之后发现for更快,为什么呢? for 更快 原因: **forEach每次都要创建一个函数来调用,而for不会创建函数,** **函数需要独立的作用域,会有额外的开销** 越“低级”的代码,性能往往越好 日常开发别只考虑性能,forEach代码可读性更好(基于时间复杂度一样的情况下,使用可读性更好的方式更佳) ## **28,前端攻击手段有哪些?该如何预防?** **XSS :**跨站脚本攻击 **DDoS  :**分布式的、大规模的流量访问,使服务器瘫痪 **CSRF  :**跨站请求伪造 **SQL 注入** ### 手写一个闭包,闭包的缺陷 ### 手写一个简单的递归,比如n+n-1+n-2+...+1 ###  简述冒泡和快排的思想 冒泡排序: > 1、冒泡排序(Bubble Sort),是一种较简单的排序算法。 > > 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。 > > 这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,所以命名为冒泡排序。 > 2、稳定性:稳定 快速排序: > 1、快速排序(Quicksort)是对冒泡排序的一种改进。 >> 快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。 >> 该方法的基本思想是: >> (1)先从数列中取出一个数作为基准数。 >> (2)分区过程,将比这个数大的数全放到它的右边,小于或等于它 的数全放到它的左边。 >> (3)再对左右区间重复第二步,直到各区间只有一个数。 > 2、稳定性:不稳定 ### 解释一下事件冒泡并自己设想一个能应用到事件冒泡的场景 ### 说出event对象的3-5个属性或方法 > 1.event.preventDefault() **如果调用这个方法,默认事件行为将不再触发** > 2.event.stopPropagation() & event.stopImmediatePropagation() event.stopPropagation()方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行 > 3.event.target指向引起触发事件的元素 > 4.event.currentTarget则是事件绑定的元素 > 5.event.type;代表事件类型;如:click > 6.event.target;表示触发事件的源头,通俗理解:点击谁触发了事件,target就是谁 > 7.event.currentTarget;表示包含事件的元素;通俗理解:事件绑定在谁身上,currentTarget就是谁 > 8.event.preventDefault();阻止默认行为;如阻止a标签的链接跳转 > 9.event.stopPropagation();阻止事件冒泡和事件捕获;(翻译propagation:传播/繁殖/蔓延) > 10.clientX/clientY;触发事件时鼠标位于浏览器窗口的固定X/Y轴坐标,不受滚动条影响; > 11.pageX/pageY;触发事件时鼠标位于浏览器窗口的X/Y轴坐标,受滚动条影响; > 12.screenX/screenY;触发事件时鼠标位于屏幕的X/Y轴坐标; ### 浏览器的缓存机制,Etag和Last-Modified存在的意义 ### 假设一个用户打开你写的网页,发现白屏了,你会如何去定位原因 ### 简述xss和csrf,如何防范,如果包含xss的内容已经被提交到了后台该怎么办 ### js中如何改变this的指向,call和apply和bind的区别 ### js如何实现数组的浅拷贝和深拷贝 ### 用过哪些js库,分别有什么特点 ### 如何将数组转换为字符串,如何将字符串转换为整数,parseInt的第二个参数代表什么 ### 有没有用过grunt gulp webpack这些,前端工程化的意义 ### 简述一下prototype,js的继承方式,比较基于构造函数继承和基于原型继承 ### xss和csrf防范 ### 事件冒泡的兼容性问题 最后: ### 两个房间,分别有三个开关和三个灯,每个房间只能进去一次,如何判断出开关对应的灯(假设一开始都是关闭的)