【愚公系列】2023年03月 其他-Web前端基础面试题(JS_高级_47道)

文章目录
  • 一、JavaScript提升篇
    • 1、什么是跨域?
    • 2、什么是原型?
    • 3、什么是闭包?
    • 4、如何防抖?
    • 5、TCP的三次握手和四次挥手
    • 6、new 操作符原理
    • 7、事件委托做了什么
    • 8、事件代理是什么
    • 9、Eventloop
    • 10、 如何实现跨域
    • 11、写出原生 Ajax
    • 12、暂时性死区是什么,举1-2例说明
    • 13、promise解决回调陷阱的链式写法
    • 14、Promise对象实现Ajax封装
    • 15、简述promise
    • 16、说说你对 proxy 的理解
    • 17、JS垃圾回收与V8垃圾回收
    • 18、什么是宏任务和微任务,两者有什么区别?
    • 19、说说 Cookie 和 Token 的区别?
    • 20、写入一个函数,将abcd以实参传入返回[1,2,3,4,5,6]
    • 21、微任务&宏任务常见笔记题一
    • 22、微任务&宏任务常见笔记题二
    • 23、前端如何优化网站性能?
    • 24、网页从输入网址到渲染完成经历了哪些过程?
    • 25、JS中常见的内存泄漏
    • 26、如何解决跨域问题
    • 27、谈谈垃圾回收机制方式及内存管理
    • 28、深拷贝、浅拷贝、以及如何实现?
    • 29、箭头函数和普通函数的区别
    • 30、同步和异步的区别
    • 31、什么叫优雅降级和渐进增强?
    • 32、请解释JSONP的工作原理,以及它为什么不是真正的AJAX
    • 33、Object.assgin()、扩展运算符(三点运算符)的区别
    • 34、介绍一下js的数据类型有哪些,值是如何存储的?
    • 35、map和Object的区别
    • 36、async 和 promise 的区别
    • 37、js常见的设计模式
    • 38、如何判断js数据类型?
    • 39、原生对象和宿主对象?
    • 40、描述new一个对象的过程
    • 41、什么是原型?什么是原型链?
    • 42、js 获取原型的方法?
    • 43、set 和map 的区别?
    • 44、说说防抖和节流
    • 45、require和import之间的区别?
    • 46、栈和堆的区别?
    • 47、forEach、for...in 、for...of三者的区别

一、JavaScript提升篇

1、什么是跨域?

跨域需要针对浏览器的同源策略来理解,同源策略指的是请求必须是同一个端口,同一个协议,同一个域名,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。

受浏览器同源策略的影响,不是同源的脚本不能操作其他源下面的对象。想要操作另一个源下的对象是就需要跨域。

2、什么是原型?

原型链:简单来讲就是原型组成的链,比如函数的原型是Function,Function的原型是Object,Object的原型仍然是Object,一直追溯到最终的原型对象。

函数通过prototype来追溯原型对象,对象通过_proto_来追溯原型对象。

通过一个构造函数创建出来的多个实例,如果都要添加一个方法,给每个实例去添加并不是一个明智的选择。这时就该用上原型了。

在实例的原型上添加一个方法,这个原型的所有实例便都有了这个方法。

原型链继承:

代码语言:javascript
复制
function A(){
    this.name="amy";
}
function B(){
    this.age="20"; //B继承了A,通过原型,形成链条
}
B.prototype=new A();
var a=new B();
console.log(a.name)//结果:amy
3、什么是闭包?

闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,它的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在外部函数调用后被自动清除。

由于局部变量无法共享和长久的保存,而全局变量可能造成变量污染,闭包的出现可以解决长久的保存变量又不会造成全局污染。

4、如何防抖?

何为防抖 多次触发事件后,事件处理函数只执行一次,并且是在触发操作结束时执行,一般用于scroll事件。

解决原理 对处理函数进行延时操作,若设定的延时到来之前再次触发事件,则清除上一次的延时操作定时器,重新定时。

代码语言:javascript
复制
let timer;
window.onscroll  = function () {
    if(timer){
        clearTimeout(timer)
    }
    timer = setTimeout(function () {
        //滚动条位置
        let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
        console.log('滚动条位置:' + scrollTop);
        timer = undefined;
    },200)
}
5、TCP的三次握手和四次挥手

TCP的三次握手和四次挥手实质就是TCP通信的连接和断开。

三次握手:为了对每次发送的数据量进行跟踪与协商,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系,并建立虚连接。

四次挥手:即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。

1、三次握手

TCP协议位于传输层,作用是提供可靠的字节流服务,为了准确无误地将数据送达目的地,TCP协议采纳三次握手策略。

三次握手原理:

第1次握手:客户端发送一个带有SYN(synchronize)标志的数据包给服务端;

第2次握手:服务端接收成功后,回传一个带有SYN/ACK标志的数据包传递确认信息,表示我收到了;

第3次握手:客户端再回传一个带有ACK标志的数据包,表示我知道了,握手结束。

2、四次挥手

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

四次挥手原理:

第1次挥手:客户端发送一个FIN,用来关闭客户端到服务端的数据传送,客户端进入FIN_WAIT_1状态;

第2次挥手:服务端收到FIN后,发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),服务端进入CLOSE_WAIT状态;

第3次挥手:服务端发送一个FIN,用来关闭服务端到客户端的数据传送,服务端进入LAST_ACK状态;

第4次挥手:客户端收到FIN后,客户端t进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,服务端进入CLOSED状态,完成四次挥手。

6、new 操作符原理
  1. 创建一个类的实例:创建一个空对象 obj,然后把这个空对象的__proto__设置为构造

函数的 prototype。

  1. 初始化实例:构造函数被传入参数并调用,关键字 this 被设定指向该实例 obj。
  2. 返回实例 obj。
7、事件委托做了什么

把一个元素响应事件(click、keydown…)的函数委托到另一个元素;

优点:减少内存消耗、动态绑定事件。

8、事件代理是什么

事件代理是利用事件的冒泡原理来实现的,何为事件冒泡呢?就是事件从最深的节点开

始,然后逐步向上传播事件,举个例子:页面上有这么一个节点树,div>ul>li>a;比如给

最里面的 a 加一个 click 点击事件,那么这个事件就会一层一层的往外执行,执行顺序

a>li>ul>div,有这样一个机制,那么我们给最外面的 div 加点击事件,那么里面的 ul,li,

a 做点击事件的时候,都会冒泡到最外层的 div 上,所以都会触发,这就是事件代理,

代理它们父级代为执行事件。

9、Eventloop

任务队列中,在每一次事件循环中,macrotask 只会提取一个执行,而 microtask 会一直

提取,直到 microsoft 队列为空为止。

也就是说如果某个 microtask 任务被推入到执行中,那么当主线程任务执行完成后,会

循环调用该队列任务中的下一个任务来执行,直到该任务队列到最后一个任务为止。而

事件循环每次只会入栈一个 macrotask,主线程执行完成该任务后又会检查 microtasks 队

列并完成里面的所有任务后再执行 macrotask 的任务。macrotasks: setTimeout, setInterval,

setImmediate, I/O, UI rendering

microtasks: process.nextTick, Promise, MutationObserver

10、 如何实现跨域

JSONP:通过动态创建 script,再请求一个带参网址实现跨域通信。document.domain +

iframe 跨域:两个页面都通过 js 强制设置 document.domain 为基础主域,就实现了同域。

location.hash + iframe 跨域:a 欲与 b 跨域相互通信,通过中间页 c 来实现。 三个页面,

不同域之间利用 iframe 的 location.hash 传值,相同域之间直接 js 访问来通信。

window.name + iframe跨域:通过iframe的src属性由外域转向本地域,跨域数据即由iframe

的 window.name 从外域传递到本地域。

postMessage 跨域:可以跨域操作的 window 属性之一。

CORS:服务端设置 Access-Control-Allow-Origin 即可,前端无须设置,若要带 cookie 请

求,前后端都需要设置。

代理跨域:起一个代理服务器,实现数据的转发

11、写出原生 Ajax

Ajax 能够在不重新加载整个页面的情况下与服务器交换数据并更新部分网页内容,实现

局部刷新,大大降低了资源的浪费,是一门用于快速创建动态网页的技术,ajax 的使用

分为四部分:

1、创建 XMLHttpRequest 对象 var xhr = new XMLHttpRequest();

2、向服务器发送请求,使用 xmlHttpRequest 对象的 open 和 send 方法,

3、监听状态变化,执行相应回调函数

var xhr = new XMLHttpRequest();

xhr.open(‘get’, ‘aabb.php’, true);

xhr.send(null);

xhr.onreadystatechange = function() {

if(xhr.readyState==4) {

if(xhr.status==200) {

console.log(xhr.responseText);

}

}

}

12、暂时性死区是什么,举1-2例说明

function f3(i){

​ let i;

​ console.log(i)

};

f3(10)

let x = y,y = 10;

function f2(){

​ console.log(x,y)

};

13、promise解决回调陷阱的链式写法
代码语言:javascript
复制
let p = new Promise(function(resolve, reject){
  if (true){
​    resolve("成功");
   } else {
​    reject("失败");
   }
})
p.then(function(v){
  return new Promise(function(resolve, reject){
​    if (true){
​      resolve("成功");
​     } else {
​      reject("失败");
​     }
  })
}, function(v){
  console.log(v)
}).then(function(){
  console.log(4)
}, function(){
  console.log(5)
})
14、Promise对象实现Ajax封装
代码语言:javascript
复制
const getJSON = function(url,type, data) {
  const promise = new Promise(function(resolve, reject){ 
   const xmlHttp = new XMLHttpRequest();
   xmlHttp.open(type, url);
   if(type =='get'){
​      xmlHttp.send();
   }else {
​     xmlHttp.setRequestHeader("Content-Type", "application/json");
​     xmlHttp.send(JSON.stringify(data));
   };
   xmlHttp.responseType = "json";
   xmlHttp.onreadystatechange =function(){
​    if (xmlHttp.readyState !== 4) return;
​    if (xmlHttp.status === 200) {
​     resolve(xmlHttp.response);
​    } else {
​     reject(new Error(xmlHttp.statusText));
​    }
   };
  });
  return promise;
};
15、简述promise

Promise 对象是 CommonJS 工作组提出的一种规范,目的是为异步编程提供统一接口。每

一个异步任务返回一个 Promise 对象,该对象有一个 then 方法,允许指定回调函数。

f1().then(f2);

一个 promise 可能有三种状态:等待(pending)、已完成(resolved,又称 fulfilled)、

已拒绝(rejected)。

promise 必须实现 then 方法(可以说,then 就是 promise 的核心),而且 then 必须返回

一个 promise,同一个 promise 的 then 可以调用多次,并且回调的执行顺序跟它们被定义

时的顺序一致。

then 方法接受两个参数,第一个参数是成功时的回调,在 promise 由“等待”态转换到

“完成”态时调用,另一个是失败时的回调,在 promise 由“等待”态转换到“拒绝”态时调用。同时,then 可以接受另一个 promise 传入,也接受一个“类 then”的对象或

方法,即 thenable 对象

16、说说你对 proxy 的理解

vue 的数据劫持有两个缺点:

1、无法监听通过索引修改数组的值的变化

2、无法监听 object 也就是对象的值的变化

所以 vue2.x 中才会有$set 属性的存在

proxy 是 es6 中推出的新 api,可以弥补以上两个缺点,所以 vue3.x 版本用 proxy 替换

object.defineproperty。

17、JS垃圾回收与V8垃圾回收

js中垃圾回收一般采用标识清除法和引用计数法

v8中垃圾回收采用分代回收:新生代,老生代和大对象

18、什么是宏任务和微任务,两者有什么区别?

ES6 规范中,宏任务(macrotask)又称为 task,微任务(microtask)又称为 jobs。

宏任务是由宿主发起的,而微任务是由 JS 本身发起。比如,宏任务有:setTimeOut、setInterval、文件操作等;微任务有:Promise.then、Promise.catch等。

整个JS在运行过程中主要执行以下事件循环(Even loop):

代码语言:javascript
复制
    主程序从上往下执行同步任务
异步任务会被放入异步任务队列中

当同步任务执行完成后,会去异步任务队列中执行异步事件</code></pre></div></div><p>在异步任务中还有宏任务和微任务的区别:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    宏任务:setTimeout、setInterval、Ajax、DOM事件等

微任务:promise、async/await、Object.observe等</code></pre></div></div><p>宏任务和微任务的执行顺序:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    同步任务-&gt;微任务-&gt;宏任务</code></pre></div></div><p>两者的区别:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    宏任务:DOM渲染后触发,如setTimeout

微任务:DOM渲染前触发,如promise</code></pre></div></div><h5 id="98ber" name="19%E3%80%81%E8%AF%B4%E8%AF%B4-Cookie-%E5%92%8C-Token-%E7%9A%84%E5%8C%BA%E5%88%AB%EF%BC%9F">19、说说 Cookie 和 Token 的区别?</h5><p><strong>为什么要有 Cookie 呢?</strong></p><p>我们都知道一般接口但是通过 HTTP 协议来进行数据交换的,而 HTTP 协议的特点是,无状态,工作前通过三次握手建立连接,工作完成后立刻通过四次挥手断开连接,每次连接都是独立存在的,没有任何状态将请求串联成一个整体,因此每次都需要重新验证是身份,即耗费了性能,也给黑客的攻击留下隐患。</p><p>Cookie 的出现,是来弥补 HTTP 无状态的问题的,Cookie 可以作为一个状态保存的状态机,用来保存用户的相关登录状态,当第一次验证通过后,服务器可以通过 set-cookie 令客户端将自己的 cookie 保存起来,当下一次再发送请求的时候,直接带上 cookie 即可,而服务器检测到客户端发送的 cookie 与其保存的 cookie 值保持一致时,则直接信任该连接,不再进行验证操作。</p><p><strong>Token</strong></p><p>Token, 令牌,代表执行某些操作的权利的对象,简单来说,就是类似 cookie 的一种验证信息,客户端通过登录验证后,服务器会返回给客户端一个加密的 token,然后当客户端再次向服务器发起连接时,带上token,服务器直接对token进行校验即可完成权限校验。</p><p><strong>token相对cookie的优势:</strong>

1、支持跨域访问 ,将token置于请求头中,而cookie是不支持跨域访问的;

2、无状态化, 服务端无需存储token ,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session;

3、 无需绑定到一个特殊的身份验证 方案(传统的用户名密码登陆),只需要生成的token是符合我们预期设定的即可;

4、 更适用于移动端 (Android,iOS,小程序等等),像这种原生平台不支持cookie;

5、 避免CSRF跨站伪造攻击;

6、 非常适用于RESTful API ,这样可以轻易与各种后端(java,.net,python…)相结合,去耦合

20、写入一个函数,将abcd以实参传入返回[1,2,3,4,5,6]

var a = 1, b = [2,3],c=[4,5],d=6;

答:

function f(…items) {

​ console.log(items);

console.log(…items);

}

var a = 1, b = [2,3],c=[4,5],d=6;

f(a, …b, …c, d);

21、微任务&宏任务常见笔记题一
代码语言:javascript
复制
async function f() {
console.log(1);
return 2
}
f().then(result => {
console.log(result);
})
console.log(3);
请写出解析的结果:

结果:

1 3 2

22、微任务&宏任务常见笔记题二
代码语言:javascript
复制
setTimeout(()=>{  
console.log(1)
},200)
setTimeout(()=>{  
console.log(2)
},100)
new Promise(function(resolve, reject){  
resolve(4)
console.log(3)
}).then(function(d){
console.log(d)
})
console.log(5);
请写出解析的结果:

结果:

35421

23、前端如何优化网站性能?

1、减少 HTTP 请求数量

代码语言:javascript
复制
   在浏览器与服务器进行通信时,主要是通过 HTTP 进行通信。浏览器与服务器需要经过三次握手,每次握手需要花费大量时间。而且不同浏览器对资源文件并发请求数量有限(不同浏览器允许并发数),一旦 HTTP 请求数量达到一定数量,资源请求就存在等待状态,这是很致命的,因此减少 HTTP 的 请求数量可以很大程度上对网站性能进行优化。

CSS Sprites:国内俗称CSS精灵,这是将多张图片合并成一张图片达到减少HTTP请求的一种解决方案,可以通过CSS的background属性来访问图片内容。这种方案同时还可以减少图片总字节数。

合并 CSS 和 JS 文件: 现在前端有很多工程化打包工具,如:grunt、gulp、webpack等。为了减少 HTTP 请求数量,可以通过这些工具再发布前将多个CSS或者多个JS合并成一个文件。

采用 lazyLoad: 俗称懒加载,可以控制网页上的内容在一开始无需加载,不需要发请求,等到用户操作真正需要的时候立即加载出内容。这样就控制了网页资源一次性请求数量。</code></pre></div></div><p>2、控制资源文件加载优先级</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0"> 浏览器在加载HTML内容时,是将HTML内容从上至下依次解析,解析到link或者script标签就会加载href或者src对应链接内容,为了第一时间展示页面给用户,就需要将CSS提前加载,不要受 JS 加载影响。

一般情况下都是CSS在头部,JS在底部。</code></pre></div></div><p>3、利用浏览器缓存</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">浏览器缓存是将网络资源存储在本地,等待下次请求该资源时,如果资源已经存在就不需要到服务器重新请求该资源,直接在本地读取该资源。</code></pre></div></div><p>4、减少重排(Reflow)</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">基本原理:重排是DOM的变化影响到了元素的几何属性(宽和高),浏览器会重新计算元素的几何属性,会使渲染树中受到影响的部分失效,浏览器会验证 DOM 树上的所有其它结点的visibility属性,这也是Reflow低效的原因。如果Reflow的过于频繁,CPU使用率就会急剧上升。</code></pre></div></div><p>减少Reflow,如果需要在DOM操作时添加样式,尽量使用 增加class属性,而不是通过style操作样式。</p><p>5、减少 DOM 操作</p><p>6、图标使用 IconFont 替换</p><h5 id="ck09e" name="24%E3%80%81%E7%BD%91%E9%A1%B5%E4%BB%8E%E8%BE%93%E5%85%A5%E7%BD%91%E5%9D%80%E5%88%B0%E6%B8%B2%E6%9F%93%E5%AE%8C%E6%88%90%E7%BB%8F%E5%8E%86%E4%BA%86%E5%93%AA%E4%BA%9B%E8%BF%87%E7%A8%8B%EF%BC%9F">24、网页从输入网址到渲染完成经历了哪些过程?</h5><p>大致可以分为如下7步:</p><p>输入网址;</p><p>发送到DNS服务器,并获取域名对应的web服务器对应的ip地址;</p><p>与web服务器建立TCP连接;</p><p>浏览器向web服务器发送http请求;</p><p>web服务器响应请求,并返回指定url的数据(或错误信息,或重定向的新的url地址);</p><p>浏览器下载web服务器返回的数据及解析html源文件;</p><p>生成DOM树,解析css和js,渲染页面,直至显示完成;</p><h5 id="4ql78" name="25%E3%80%81JS%E4%B8%AD%E5%B8%B8%E8%A7%81%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F">25、JS中常见的内存泄漏</h5><p>1、意外的全局变量</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    函数中意外的定义了全局变量,每次执行该函数都会生成该变量,且不会随着函数执行结束而释放。</code></pre></div></div><p>2、未清除的定时器</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    定时器没有清除,它内部引用的变量,不会被释放。</code></pre></div></div><p>3、脱离DOM的元素引用</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    一个dom容器删除之后,变量未置为null,则其内部的dom元素则不会释放。</code></pre></div></div><p>4、持续绑定的事件</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    函数中addEventListener绑定事件,函数多次执行,绑定便会产生多次,产生内存泄漏。</code></pre></div></div><p>5、闭包引起内存泄漏</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    比如事件处理回调,导致DOM对象和脚本中对象双向引用。</code></pre></div></div><p>6、console.log</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    console.log的对象是不能被垃圾回收</code></pre></div></div><h5 id="90koc" name="26%E3%80%81%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98">26、如何解决跨域问题</h5><p>(1)通过jsonp跨域</p><p>(2)跨域资源共享(CORS)</p><p>(3)nginx代理跨域</p><p>(4)nodejs中间件代理跨域</p><h5 id="7t7qb" name="27%E3%80%81%E8%B0%88%E8%B0%88%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6%E6%96%B9%E5%BC%8F%E5%8F%8A%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86">27、谈谈垃圾回收机制方式及内存管理</h5><p>JavaScript 在定义变量时就完成了内存分配。当不在使用变量了就会被回收,因为其开销比较大,垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。</p><p>(1)垃圾回收</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    标记清除法

当变量进入环境时,将这个变量标记为&#39;进入环境&#39;。当标记离开环境时,标记为‘离开环境’。离开环境的变量会被回收

引用技计数法

跟踪记录每个值被引用的次数,如果没有被引用,就会回收</code></pre></div></div><p>(2)内存管理</p><p>内存分配=》内存使用=》内存回收</p><h5 id="fdfk2" name="28%E3%80%81%E6%B7%B1%E6%8B%B7%E8%B4%9D%E3%80%81%E6%B5%85%E6%8B%B7%E8%B4%9D%E3%80%81%E4%BB%A5%E5%8F%8A%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%EF%BC%9F">28、深拷贝、浅拷贝、以及如何实现?</h5><p>​ 深拷贝和浅拷贝都是针对复杂类型来说的,深拷贝式是层层拷贝,浅拷贝是只拷贝一层。</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    浅拷贝:使用object.assign、直接用=赋值只拷贝地址,它是将数据中的所有数据引用下来,指向同一个存放地址,拷贝后的数据修改后,会影响到原数据中的对象数据。

深拷贝:JSON.parse(JSON. stringify...),递归拷贝每一层对象是内容拷贝,将数据中的所有数据都拷贝下来,对拷贝后的数据进行修改,不会影响到原数据。

可以使用for...in、扩展运算符...、递归等递归函数实现深拷贝

递归:递归就是一个函数调用其本身,通过栈来实现。每执行一个函数,就新建一个函数栈。</code></pre></div></div><h5 id="bh3k4" name="29%E3%80%81%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0%E5%92%8C%E6%99%AE%E9%80%9A%E5%87%BD%E6%95%B0%E7%9A%84%E5%8C%BA%E5%88%AB">29、箭头函数和普通函数的区别</h5><p>(1)普通函数</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    可以通过bind、call、apply改变this指向

可以使用new</code></pre></div></div><p>(2)箭头函数</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    本身没有this指向,

它的this在定义的时候继承自外层第一个普通函数的this

被继承的普通函数的this指向改变,箭头函数的this指向会跟着改变

箭头函数外层没有普通函数时,this指向window

不能通过bind、call、apply改变this指向

使用new调用箭头函数会报错,因为箭头函数没有constructor</code></pre></div></div><h5 id="f1o1c" name="30%E3%80%81%E5%90%8C%E6%AD%A5%E5%92%8C%E5%BC%82%E6%AD%A5%E7%9A%84%E5%8C%BA%E5%88%AB">30、同步和异步的区别</h5><p>同步是一直阻塞模式,如果一个请求需要等待回调,那么会一直等待下去,直到返回结果</p><p>异步是非阻塞模式,无需等待回调,可执行下一步的代码</p><h5 id="cp4fe" name="31%E3%80%81%E4%BB%80%E4%B9%88%E5%8F%AB%E4%BC%98%E9%9B%85%E9%99%8D%E7%BA%A7%E5%92%8C%E6%B8%90%E8%BF%9B%E5%A2%9E%E5%BC%BA%EF%BC%9F">31、什么叫优雅降级和渐进增强?</h5><p>渐进增强(Progressive Enhancement):一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    优雅降级(Graceful Degradation):一开始就构建站点的完整功能,然后针对浏览器测试和修复。比如一开始使用 CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行 hack 使其可以在低版本浏览器上正常浏览。

其实渐进增强和优雅降级并非什么新概念,只是旧的概念换了一个新的说法。在传统软件开发中,经常会提到向上兼容和向下兼容的概念。渐进增强相当于向上兼容,而优雅降级相当于向下兼容。

区别:优雅降级是从复杂的现状开始,并试图减少用户体验的供给,而渐进增强则是从一个非常基础的,能够起作用的版本开始,并不断扩充,以适应未来环境的需要。降级(功能衰减)意味着往回看;而渐进增强则意味着朝前看,同时保证其根基处于安全地带</code></pre></div></div><h5 id="d48lk" name="32%E3%80%81%E8%AF%B7%E8%A7%A3%E9%87%8AJSONP%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%EF%BC%8C%E4%BB%A5%E5%8F%8A%E5%AE%83%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E6%98%AF%E7%9C%9F%E6%AD%A3%E7%9A%84AJAX">32、请解释JSONP的工作原理,以及它为什么不是真正的AJAX</h5><p>JSONP 是一种非正式传输协议,允许用户传递一个callback给服务端,然后服务端返回数据时会将这个callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。</p><p>当GET请求从后台页面返回时,可以返回一段JavaScript代码,这段代码会自动执行,可以用来负责调用后台页面中的一个callback函数。</p><p>为什么它不是真正的Ajax:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    它们的实质不同

ajax的核心是通过xmlHttpRequest获取非本页内容

jsonp的核心是动态添加script标签调用服务器提供的js脚本

jsonp只支持get请求,ajax支持get和post请求</code></pre></div></div><h5 id="531qm" name="33%E3%80%81Object.assgin()%E3%80%81%E6%89%A9%E5%B1%95%E8%BF%90%E7%AE%97%E7%AC%A6%EF%BC%88%E4%B8%89%E7%82%B9%E8%BF%90%E7%AE%97%E7%AC%A6%EF%BC%89%E7%9A%84%E5%8C%BA%E5%88%AB">33、Object.assgin()、扩展运算符(三点运算符)的区别</h5><p>Object.assgin()是浅拷贝</p><p>三点运算符第一层是深拷贝,其他的都是浅拷贝</p><h5 id="8uu92" name="34%E3%80%81%E4%BB%8B%E7%BB%8D%E4%B8%80%E4%B8%8Bjs%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B%EF%BC%8C%E5%80%BC%E6%98%AF%E5%A6%82%E4%BD%95%E5%AD%98%E5%82%A8%E7%9A%84%EF%BC%9F">34、介绍一下js的数据类型有哪些,值是如何存储的?</h5><p>(1)数据类型</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    基本数据类型:Number、Boolean、null、undefined、Symbol(ES6新增,表示独一无二的值)和Bigint(ES10新增)

引用数据类型:Object</code></pre></div></div><p>(2)如何存储</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    原始数据类型:直接存储在栈中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。

引用数据类型:同时存储在栈和堆中,占据空间大,大小不固定。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。</code></pre></div></div><h5 id="4bpuv" name="35%E3%80%81map%E5%92%8CObject%E7%9A%84%E5%8C%BA%E5%88%AB">35、map和Object的区别</h5><p>(1)意外的键:Map默认不包含任意键,只包含插入的键值;Object有一个原型、原型链的键名可能和自己在对象上设置的键名发生冲突;</p><p>(2)键的类型:Map键的类型是任意的;Object键的类型是string和symbol;</p><p>(3)键的顺序:Map有序的,迭代的时候以其插入的顺序返回键值;Object无序的;</p><p>(4)size:Map的长度可以通过size属性获取;Object需要手动计算;</p><p>(5)迭代:Map是可迭代的;Object需要通过获取键来迭代;</p><p>(6)性能:Map在频繁增删键值对的场景下表现更好;Object在频繁添加和删除键值对的场景下未作出优化;</p><h5 id="9nl0" name="36%E3%80%81async-%E5%92%8C-promise-%E7%9A%84%E5%8C%BA%E5%88%AB">36、async 和 promise 的区别</h5><p>(1)Async/Await 代码看起来简洁一些,使得异步代码看起来像同步代码</p><p>(2)async await与Promise一样,是非阻塞的。</p><p>(3)async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数。</p><h5 id="5ei7q" name="37%E3%80%81js%E5%B8%B8%E8%A7%81%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F">37、js常见的设计模式</h5><p>(1)单例模式</p><p>(2)工厂模式</p><p>(3)构造函数模式</p><p>(4)发布订阅者模式</p><p>(5)迭代器模式</p><p>(6)代理模式</p><h5 id="cldob" name="38%E3%80%81%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%ADjs%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%EF%BC%9F">38、如何判断js数据类型?</h5><p>(1)typeof:可以判断出string,number,boolean,undefined,symbol,function,bigint,但判断 typeof(null) 时值为 ‘object’; 判断数组和对象时值均为 ‘object’</p><p>(2)instanceof:可以判断一个实例是否属于某种类型,也可以判断一个实例是否是其父类型或者祖先类型的实例</p><p>(3)constructor:除了undefined和null之外,其他类型都可以通过constructor来判断。但如果声明了一个构造函数,并且改变了它的原型执行,这种情况下constructor也不能准确判断</p><p>(4)Object.prototype.toString:判断一个对象只属于某种内置类型,但不能准确判断一个实例是否属于某种类型</p><p>(5)Array.isArray:判断是否为数组</p><h5 id="1duq9" name="39%E3%80%81%E5%8E%9F%E7%94%9F%E5%AF%B9%E8%B1%A1%E5%92%8C%E5%AE%BF%E4%B8%BB%E5%AF%B9%E8%B1%A1%EF%BC%9F">39、原生对象和宿主对象?</h5><p>原生对象是ECMAScript规定的对象,所有内置对象都是原生对象,比如Array、 Date、 RegExp等;</p><p>宿主对象是宿主环境比如浏览器规定的对象,用于完善是ECMAScript的执行环境,比如Document、 Location、Navigator等。</p><h5 id="bbr55" name="40%E3%80%81%E6%8F%8F%E8%BF%B0new%E4%B8%80%E4%B8%AA%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%BF%87%E7%A8%8B">40、描述new一个对象的过程</h5><p>(1)创建一个新对象,新对象的隐式原型__proto__指向new的构造函数的显示原型proptotype</p><p>(2)改变this指向,将构造函数的作用域赋给新的对象,并且执行构造函数的代码,为新的对象添加属性</p><p>(3)返回新的对象(return this)</p><h5 id="f1qdu" name="41%E3%80%81%E4%BB%80%E4%B9%88%E6%98%AF%E5%8E%9F%E5%9E%8B%EF%BC%9F%E4%BB%80%E4%B9%88%E6%98%AF%E5%8E%9F%E5%9E%8B%E9%93%BE%EF%BC%9F">41、什么是原型?什么是原型链?</h5><p>​ 原型:每个构造函数都有一个原型对象,实例化出来的对象都有一个原型,指向的是构造函数的原型对象,原型对象里面有一个指针constructor,指向的是它的构造函数。所有的原型对象都是Object构造函数的实例。它的原型指的就是Object原型对象,在往上找Object原型对象的原型就是null了。所有的构造函数都是function构造函数的实例,所有构造函数的原型指的就是function原型对象。</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">    原型链:当在实例化的对象中访问一个属性时,首先会在该对象内部(自身属性)寻找,如找不到,则会向其__proto__指向的原型中寻找,如仍找不到,则继续向原型中__proto__指向的上级原型中寻找,直至找到或Object.prototype.__proto__为止(值为null),这种链状过程即为原型链。</code></pre></div></div><p>原型的作用:</p><p>1.数据共享 节约内存内存空间</p><p>2.实现继承</p><h5 id="1eeh5" name="42%E3%80%81js-%E8%8E%B7%E5%8F%96%E5%8E%9F%E5%9E%8B%E7%9A%84%E6%96%B9%E6%B3%95%EF%BC%9F">42、js 获取原型的方法?</h5><p>(1)Object.getPrototypeOf()</p><p>(2)<strong>proto</strong></p><p>(3)constructor.prototype</p><h5 id="2or7u" name="43%E3%80%81set-%E5%92%8Cmap-%E7%9A%84%E5%8C%BA%E5%88%AB%EF%BC%9F">43、set 和map 的区别?</h5><p>(1)Map是键值对,Set是值的集合,键和值可以是任何的值;</p><p>(2)Map可以通过get方法获取值,而set不能因为它只有值,set只能用has来判断,返回一个布尔值;</p><p>(3)Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储</p><h5 id="edo84" name="44%E3%80%81%E8%AF%B4%E8%AF%B4%E9%98%B2%E6%8A%96%E5%92%8C%E8%8A%82%E6%B5%81">44、说说防抖和节流</h5><p>防抖:n秒后在执行该事件,若在n秒内被重复触发,则重新计时</p><p>节流: n秒内只运行一次,若在n秒内重复触发,只有一次生效</p><h5 id="9en8t" name="45%E3%80%81require%E5%92%8Cimport%E4%B9%8B%E9%97%B4%E7%9A%84%E5%8C%BA%E5%88%AB%EF%BC%9F">45、require和import之间的区别?</h5><p>(1)require对应导出的方法是module.exports,import对应的方法是export default/export</p><p>(2)require 是CommonJs的语法,import 是 ES6 的语法标准。</p><p>(3)require是运行运行时加载模块里的所有方法(动态加载),import 是编译的时候调用(静态加载),不管在哪里引用都会提升到代码顶部。</p><p>(4)require 是CommonJs的语法,引入的是的是整个模块里面的对象,import 可以按需引入模块里面的对象</p><p>(5)require 导出是值的拷贝,import 导出的是值的引用</p><h5 id="6df9m" name="46%E3%80%81%E6%A0%88%E5%92%8C%E5%A0%86%E7%9A%84%E5%8C%BA%E5%88%AB%EF%BC%9F">46、栈和堆的区别?</h5><p>(1)申请方式的不同。栈由系统自动分配,而堆是人为申请开辟;</p><p>(2)申请大小的不同。栈获得的空间较小,而堆获得的空间较大;</p><p>(3)申请效率的不同。栈由系统自动分配,速度较快,而堆一般速度比较慢;</p><p>(4)存储内容的不同。栈在函数调用时,函数调用语句的下一条可执行语句的地址第一个进栈,然后函数的各个参数进栈,其中静态变量是不入栈的。而堆一般是在头部用一个字节存放堆的大小,堆中的具体内容是人为安排;</p><p>(5)底层不同。栈是连续的空间,而堆是不连续的空间。</p><p>(6)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。</p><p>(7)管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;</p><h5 id="fvmhq" name="47%E3%80%81forEach%E3%80%81for%E2%80%A6in-%E3%80%81for%E2%80%A6of%E4%B8%89%E8%80%85%E7%9A%84%E5%8C%BA%E5%88%AB">47、forEach、for…in 、for…of三者的区别</h5><p>(1)forEach遍历数组,但不能使用break、continue和return语句</p><p>(2)for…in是用来循环带有字符串key的对象的方法。实际是为循环”enumerable“(可枚举)对象而设计的。Js基本数据类型自带的原型属性不可枚举,通过Object.defineProperty0方法指定enumeralbe为false的属性不可枚举。</p><p>(3)for…in循环出的是key,for…of循环出的是value。</p><p>(4)for…of数组对象都可以遍历,它是ES6中新增加的语法。一个数据结构只有部署了 Symbol.iterator 属性, 才具有 iterator接口可以使用 for…of循环。for…of遍历对象需要通过和Object.keys()</p>