乐趣区

关于javascript:高级前端一面面试题集锦

具体阐明 Event loop

家喻户晓 JS 是门非阻塞单线程语言,因为在最后 JS 就是为了和浏览器交互而诞生的。如果 JS 是门多线程的语言话,咱们在多个线程中解决 DOM 就可能会产生问题(一个线程中新加节点,另一个线程中删除节点),当然能够引入读写锁解决这个问题。

JS 在执行的过程中会产生执行环境,这些执行环境会被程序的退出到执行栈中。如果遇到异步的代码,会被挂起并退出到 Task(有多种 task)队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出须要执行的代码并放入执行栈中执行,所以实质上来说 JS 中的异步还是同步行为。

console.log('script start');

setTimeout(function() {console.log('setTimeout');
}, 0);

console.log('script end');

以上代码尽管 setTimeout 延时为 0,其实还是异步。这是因为 HTML5 标准规定这个函数第二个参数不得小于 4 毫秒,有余会主动减少。所以 setTimeout 还是会在 script end 之后打印。

不同的工作源会被调配到不同的 Task 队列中,工作源能够分为 微工作(microtask)和 宏工作(macrotask)。在 ES6 标准中,microtask 称为 jobs,macrotask 称为 task

console.log('script start');

setTimeout(function() {console.log('setTimeout');
}, 0);

new Promise((resolve) => {console.log('Promise')
    resolve()}).then(function() {console.log('promise1');
}).then(function() {console.log('promise2');
});

console.log('script end');
// script start => Promise => script end => promise1 => promise2 => setTimeout

以上代码尽管 setTimeout 写在 Promise 之前,然而因为 Promise 属于微工作而 setTimeout 属于宏工作,所以会有以上的打印。

微工作包含 process.nextTickpromiseObject.observeMutationObserver

宏工作包含 scriptsetTimeoutsetIntervalsetImmediateI/OUI rendering

很多人有个误区,认为微工作快于宏工作,其实是谬误的。因为宏工作中包含了 script,浏览器会先执行一个宏工作,接下来有异步代码的话就先执行微工作。

所以正确的一次 Event loop 程序是这样的

  1. 执行同步代码,这属于宏工作
  2. 执行栈为空,查问是否有微工作须要执行
  3. 执行所有微工作
  4. 必要的话渲染 UI
  5. 而后开始下一轮 Event loop,执行宏工作中的异步代码

通过上述的 Event loop 程序可知,如果宏工作中的异步代码有大量的计算并且须要操作 DOM 的话,为了更快的 界面响应,咱们能够把操作 DOM 放入微工作中。

Node 中的 Event loop

Node 中的 Event loop 和浏览器中的不雷同。

Node 的 Event loop 分为 6 个阶段,它们会依照程序重复运行

┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<──connections───     │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘
timer

timers 阶段会执行 setTimeoutsetInterval

一个 timer 指定的工夫并不是精确工夫,而是在达到这个工夫后尽快执行回调,可能会因为零碎正在执行别的事务而提早。

上限的工夫有一个范畴:[1, 2147483647],如果设定的工夫不在这个范畴,将被设置为 1。

I/O

I/O 阶段会执行除了 close 事件,定时器和 setImmediate 的回调

idle, prepare

idle, prepare 阶段外部实现

poll

poll 阶段很重要,这一阶段中,零碎会做两件事件

  1. 执行到点的定时器
  2. 执行 poll 队列中的事件

并且当 poll 中没有定时器的状况下,会发现以下两件事件

  • 如果 poll 队列不为空,会遍历回调队列并同步执行,直到队列为空或者零碎限度
  • 如果 poll 队列为空,会有两件事产生

    • 如果有 setImmediate 须要执行,poll 阶段会进行并且进入到 check 阶段执行 setImmediate
    • 如果没有 setImmediate 须要执行,会期待回调被退出到队列中并立刻执行回调

如果有别的定时器须要被执行,会回到 timer 阶段执行回调。

check

check 阶段执行 setImmediate

close callbacks

close callbacks 阶段执行 close 事件

并且在 Node 中,有些状况下的定时器执行程序是随机的

setTimeout(() => {console.log('setTimeout');
}, 0);
setImmediate(() => {console.log('setImmediate');
})
// 这里可能会输入 setTimeout,setImmediate
// 可能也会相同的输入,这取决于性能
// 因为可能进入 event loop 用了不到 1 毫秒,这时候会执行 setImmediate
// 否则会执行 setTimeout

当然在这种状况下,执行程序是雷同的

var fs = require('fs')

fs.readFile(__filename, () => {setTimeout(() => {console.log('timeout');
    }, 0);
    setImmediate(() => {console.log('immediate');
    });
});
// 因为 readFile 的回调在 poll 中执行
// 发现有 setImmediate,所以会立刻跳到 check 阶段执行回调
// 再去 timer 阶段执行 setTimeout
// 所以以上输入肯定是 setImmediate,setTimeout

下面介绍的都是 macrotask 的执行状况,microtask 会在以上每个阶段实现后立刻执行。

setTimeout(()=>{console.log('timer1')

    Promise.resolve().then(function() {console.log('promise1')
    })
}, 0)

setTimeout(()=>{console.log('timer2')

    Promise.resolve().then(function() {console.log('promise2')
    })
}, 0)

// 以上代码在浏览器和 node 中打印状况是不同的
// 浏览器中打印 timer1, promise1, timer2, promise2
// node 中打印 timer1, timer2, promise1, promise2

Node 中的 process.nextTick 会先于其余 microtask 执行。

setTimeout(() => {console.log("timer1");

  Promise.resolve().then(function() {console.log("promise1");
  });
}, 0);

process.nextTick(() => {console.log("nextTick");
});
// nextTick, timer1, promise1

文档申明(Doctype)和 <!Doctype html> 有何作用? 严格模式与混淆模式如何辨别?它们有何意义?

文档申明的作用: 文档申明是为了通知浏览器,以后 HTML 文档应用什么版本的 HTML 来写的,这样浏览器能力依照申明的版本来正确的解析。

的作用:<!doctype html> 的作用就是让浏览器进入规范模式,应用最新的 HTML5 规范来解析渲染页面;如果不写,浏览器就会进入混淆模式,咱们须要防止此类情况产生。

严格模式与混淆模式的辨别:

  • 严格模式 :又称为规范模式,指浏览器依照W3C 规范解析代码;
  • 混淆模式:又称怪异模式、兼容模式,是指浏览器用本人的形式解析代码。混淆模式通常模仿老式浏览器的行为,以避免老站点无奈工作;

辨别 :网页中的DTD,间接影响到应用的是严格模式还是浏览模式,能够说DTD 的应用与这两种形式的区别非亲非故。

  • 如果文档蕴含严格的 DOCTYPE,那么它个别以严格模式出现( 严格 DTD ——严格模式);
  • 蕴含过渡 DTDURIDOCTYPE,也以严格模式出现,但有过渡 DTD 而没有 URI(对立资源标识符,就是申明最初的地址)会导致页面以混淆模式出现(有 URI 的过渡 DTD ——严格模式;没有 URI 的过渡 DTD ——混淆模式);
  • DOCTYPE 不存在或模式不正确会导致文档以混淆模式出现(DTD 不存在或者格局不正确——混淆模式);
  • HTML5 没有 DTD,因而也就没有严格模式与混淆模式的区别,HTML5 有绝对宽松的 法,实现时,曾经尽可能大的实现了向后兼容(HTML5 没有严格和混淆之分)。

总之,严格模式让各个浏览器对立执行一套标准兼容模式保障了旧网站的失常运行。

如何解释 React 的渲染流程

  • React 的渲染过程大抵统一,但协调并不相同,以 React 16 为分界线,分为 Stack ReconcilerFiber Reconciler。这里的协调从广义上来讲,特指 React 的 diff 算法,狭义上来讲,有时候也指 React 的 reconciler 模块,它通常蕴含了 diff 算法和一些公共逻辑。
  • 回到 Stack Reconciler 中,Stack Reconciler 外围调度形式是递归 调度的根本解决单位是事务 ,它的事务基类是 Transaction,这里的 事务是 React 团队从后端开发中退出的概念 。在 React 16 以前, 挂载次要通过 ReactMount 模块实现,更新通过 ReactUpdate 模块实现,模块之间互相拆散,落脚执行点也是事务。
  • React 16 及当前,协调改为了 Fiber Reconciler。它的调度形式次要有两个特点,第一个是合作式多任务模式 ,在这个模式下,线程会定时放弃本人的运行权力,交还给主线程,通过requestIdleCallback 实现。 第二个特点是策略优先级 ,调度工作通过标记 tag 的形式分优先级执行,比方动画,或者标记为 high 的工作能够优先执行。Fiber Reconciler 的根本单位是 FiberFiber 基于过来的 React Element 提供了二次封装,提供了指向父、子、兄弟节点的援用,为 diff 工作的双链表实现提供了根底。
  • 在新的架构下,整个生命周期被划分为 Render 和 Commit 两个阶段 Render 阶段的执行特点是可中断、可进行、无副作用,次要是通过结构 workInProgress 树计算出 diff。以 current 树为根底,将每个 Fiber 作为一个根本单位,自下而上一一节点查看并结构 workInProgress 树。这个过程不再是递归,而是基于循环来实现
  • 在执行上通过 requestIdleCallback 来调度执行每组工作,每组中的每个计算工作被称为 work,每个 work 实现后确认是否有优先级更高的 work 须要插入,如果有就让位,没有就持续。优先级通常是标记为动画或者 high 的会先解决。每实现一组后,将调度权交回主线程,直到下一次 requestIdleCallback 调用,再持续构建 workInProgress
  • commit 阶段须要解决 effect 列表,这里的 effect 列表蕴含了依据 diff 更新 DOM 树 回调生命周期 响应 ref 等。
  • 但肯定要留神,这个阶段是同步执行的,不可中断暂停,所以不要在 componentDidMountcomponentDidUpdatecomponentWiilUnmount中去执行重度耗费算力的工作
  • 如果只是个别的利用场景,比方治理后盾、H5 展现页等,两者性能差距并不大,但在动画、画布及手势等场景下,Stack Reconciler 的设计会占用占主线程,造成卡顿,而 fiber reconciler 的设计则能带来高性能的体现

箭头函数的 this 指向哪⾥?

箭头函数不同于传统 JavaScript 中的函数,箭头函数并没有属于⾃⼰的 this,它所谓的 this 是捕捉其所在高低⽂的 this 值,作为⾃⼰的 this 值,并且因为没有属于⾃⼰的 this,所以是不会被 new 调⽤的,这个所谓的 this 也不会被扭转。

能够⽤ Babel 了解⼀下箭头函数:

// ES6 
const obj = {getArrow() {return () => {console.log(this === obj); 
    }; 
  } 
}

转化后:

// ES5,由 Babel 转译
var obj = {getArrow: function getArrow() { 
     var _this = this; 
     return function () {console.log(_this === obj); 
     }; 
   } 
};

响应式设计的概念及基本原理

响应式网站设计(Responsive Web design)是一个网站可能兼容多个终端,而不是为每一个终端做一个特定的版本。

对于原理:基本原理是通过媒体查问 (@media) 查问检测不同的设施屏幕尺寸做解决。
对于兼容:页面头部必须有 mate 申明的viewport

<meta name="’viewport’" content="”width=device-width," initial-scale="1." maximum-scale="1,user-scalable=no”"/>

typeof null 的后果是什么,为什么?

typeof null 的后果是 Object。

在 JavaScript 第一个版本中,所有值都存储在 32 位的单元中,每个单元蕴含一个小的 类型标签(1-3 bits) 以及以后要存储值的实在数据。类型标签存储在每个单元的低位中,共有五种数据类型:

000: object   - 以后存储的数据指向一个对象。1: int      - 以后存储的数据是一个 31 位的有符号整数。010: double   - 以后存储的数据指向一个双精度的浮点数。100: string   - 以后存储的数据指向一个字符串。110: boolean  - 以后存储的数据是布尔值。

如果最低位是 1,则类型标签标记位的长度只有一位;如果最低位是 0,则类型标签标记位的长度占三位,为存储其余四种数据类型提供了额定两个 bit 的长度。

有两种非凡数据类型:

  • undefined 的值是 (-2)30(一个超出整数范畴的数字);
  • null 的值是机器码 NULL 指针(null 指针的值全是 0)

那也就是说 null 的类型标签也是 000,和 Object 的类型标签一样,所以会被断定为 Object。

参考 前端进阶面试题具体解答

对 Flex 布局的了解及其应用场景

Flex 是 FlexibleBox 的缩写,意为 ” 弹性布局 ”,用来为盒状模型提供最大的灵活性。任何一个容器都能够指定为 Flex 布局。行内元素也能够应用 Flex 布局。留神,设为 Flex 布局当前,子元素的 float、clear 和 vertical-align 属性将生效。采纳 Flex 布局的元素,称为 Flex 容器(flex container),简称 ” 容器 ”。它的所有子元素主动成为容器成员,称为 Flex 我的项目(flex item),简称 ” 我的项目 ”。容器默认存在两根轴:程度的主轴(main axis)和垂直的穿插轴(cross axis),我的项目默认沿程度主轴排列。

以下 6 个属性设置在 容器上

  • flex-direction 属性决定主轴的方向(即我的项目的排列方向)。
  • flex-wrap 属性定义,如果一条轴线排不下,如何换行。
  • flex-flow 属性是 flex-direction 属性和 flex-wrap 属性的简写模式,默认值为 row nowrap。
  • justify-content 属性定义了我的项目在主轴上的对齐形式。
  • align-items 属性定义我的项目在穿插轴上如何对齐。
  • align-content 属性定义了多根轴线的对齐形式。如果我的项目只有一根轴线,该属性不起作用。

以下 6 个属性设置在 我的项目上

  • order 属性定义我的项目的排列程序。数值越小,排列越靠前,默认为 0。
  • flex-grow 属性定义我的项目的放大比例,默认为 0,即如果存在残余空间,也不放大。
  • flex-shrink 属性定义了我的项目的放大比例,默认为 1,即如果空间有余,该我的项目将放大。
  • flex-basis 属性定义了在调配多余空间之前,我的项目占据的主轴空间。浏览器依据这个属性,计算主轴是否有多余空间。它的默认值为 auto,即我的项目的原本大小。
  • flex 属性是 flex-grow,flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。
  • align-self 属性容许单个我的项目有与其余我的项目不一样的对齐形式,可笼罩 align-items 属性。默认值为 auto,示意继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch。

简略来说: flex 布局是 CSS3 新增的一种布局形式,能够通过将一个元素的 display 属性值设置为 flex 从而使它成为一个 flex 容器,它的所有子元素都会成为它的我的项目。一个容器默认有两条轴:一个是程度的主轴,一个是与主轴垂直的穿插轴。能够应用 flex-direction 来指定主轴的方向。能够应用 justify-content 来指定元素在主轴上的排列形式,应用 align-items 来指定元素在穿插轴上的排列形式。还能够应用 flex-wrap 来规定当一行排列不下时的换行形式。对于容器中的我的项目,能够应用 order 属性来指定我的项目的排列程序,还能够应用 flex-grow 来指定当排列空间有残余的时候,我的项目的放大比例,还能够应用 flex-shrink 来指定当排列空间有余时,我的项目的放大比例。

如何取得对象非原型链上的属性?

应用后 hasOwnProperty() 办法来判断属性是否属于原型链的属性:

function iterate(obj){var res=[];
   for(var key in obj){if(obj.hasOwnProperty(key))
           res.push(key+':'+obj[key]);
   }
   return res;
} 

Proxy 能够实现什么性能?

在 Vue3.0 中通过 Proxy 来替换本来的 Object.defineProperty 来实现数据响应式。

Proxy 是 ES6 中新增的性能,它能够用来自定义对象中的操作。

let p = new Proxy(target, handler)

target 代表须要增加代理的对象,handler 用来自定义对象中的操作,比方能够用来自定义 set 或者 get 函数。

上面来通过 Proxy 来实现一个数据响应式:

let onWatch = (obj, setBind, getLogger) => {
  let handler = {get(target, property, receiver) {getLogger(target, property)
      return Reflect.get(target, property, receiver)
    },
    set(target, property, value, receiver) {setBind(value, property)
      return Reflect.set(target, property, value)
    }
  }
  return new Proxy(obj, handler)
}
let obj = {a: 1}
let p = onWatch(
  obj,
  (v, property) => {console.log(` 监听到属性 ${property}扭转为 ${v}`)
  },
  (target, property) => {console.log(`'${property}' = ${target[property]}`)
  }
)
p.a = 2 // 监听到属性 a 扭转
p.a // 'a' = 2

在上述代码中,通过自定义 setget 函数的形式,在本来的逻辑中插入了咱们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。

当然这是简略版的响应式实现,如果须要实现一个 Vue 中的响应式,须要在 get 中收集依赖,在 set 派发更新,之所以 Vue3.0 要应用 Proxy 替换本来的 API 起因在于 Proxy 无需一层层递归为每个属性增加代理,一次即可实现以上操作,性能上更好,并且本来的实现有一些数据更新不能监听到,然而 Proxy 能够完满监听到任何形式的数据扭转,惟一缺点就是浏览器的兼容性不好。

渐进加强和优雅降级之间的区别

(1)渐进加强(progressive enhancement):次要是针对低版本的浏览器进行页面重构,保障根本的性能状况下,再针对高级浏览器进行成果、交互等方面的改良和追加性能,以达到更好的用户体验。(2)优雅降级 graceful degradation:一开始就构建残缺的性能,而后再针对低版本的浏览器进行兼容。

两者区别:

  • 优雅降级是从简单的现状开始的,并试图缩小用户体验的供应;而渐进加强是从一个十分根底的,可能起作用的版本开始的,并在此基础上一直裁减,以适应将来环境的须要;
  • 降级(性能衰竭)意味着往回看,而渐进加强则意味着往前看,同时保障其根基处于平安地带。

“优雅降级”观点认为应该针对那些最高级、最欠缺的浏览器来设计网站。而将那些被认为“过期”或有性能缺失的浏览器下的测试工作安顿在开发周期的最初阶段,并把测试对象限定为支流浏览器(如 IE、Mozilla 等)的前一个版本。在这种设计范例下,旧版的浏览器被认为仅能提供“简陋却不妨 (poor, but passable)”的浏览体验。能够做一些小的调整来适应某个特定的浏览器。但因为它们并非咱们所关注的焦点,因而除了修复较大的谬误之外,其它的差别将被间接疏忽。

“渐进加强”观点则认为应关注于内容自身。内容是建设网站的诱因,有的网站展现它,有的则收集它,有的寻求,有的操作,还有的网站甚至会蕴含以上的种种,但相同点是它们全都波及到内容。这使得“渐进加强”成为一种更为正当的设计范例。这也是它立刻被 Yahoo 所驳回并用以构建其“分级式浏览器反对 (Graded Browser Support)”策略的起因所在。

head 标签有什么作用,其中什么标签必不可少?

标签用于定义文档的头部,它是所有头部元素的容器。中的元素能够援用脚本、批示浏览器在哪里找到样式表、提供元信息等。

文档的头部形容了文档的各种属性和信息,包含文档的题目、在 Web 中的地位以及和其余文档的关系等。绝大多数文档头部蕴含的数据都不会真正作为内容显示给读者。

上面这些标签可用在 head 局部:<base>, <link>, <meta>, <script>, <style>, <title>

其中 <title> 定义文档的题目,它是 head 局部中惟一必须的元素。

null 和 undefined 区别

首先 Undefined 和 Null 都是根本数据类型,这两个根本数据类型别离都只有一个值,就是 undefined 和 null。

undefined 代表的含意是 未定义 ,null 代表的含意是 空对象。个别变量申明了但还没有定义的时候会返回 undefined,null 次要用于赋值给一些可能会返回对象的变量,作为初始化。

undefined 在 JavaScript 中不是一个保留字,这意味着能够应用 undefined 来作为一个变量名,然而这样的做法是十分危险的,它会影响对 undefined 值的判断。咱们能够通过一些办法取得平安的 undefined 值,比如说 void 0。

当对这两种类型应用 typeof 进行判断时,Null 类型化会返回“object”,这是一个历史遗留的问题。当应用双等号对两种类型的值进行比拟时会返回 true,应用三个等号时会返回 false。

异步编程的实现形式?

JavaScript 中的异步机制能够分为以下几种:

  • 回调函数 的形式,应用回调函数的形式有一个毛病是,多个回调函数嵌套的时候会造成回调函数天堂,高低两层的回调函数间的代码耦合度太高,不利于代码的可保护。
  • Promise 的形式,应用 Promise 的形式能够将嵌套的回调函数作为链式调用。然而应用这种办法,有时会造成多个 then 的链式调用,可能会造成代码的语义不够明确。
  • generator 的形式,它能够在函数的执行过程中,将函数的执行权转移进来,在函数内部还能够将执行权转移回来。当遇到异步函数执行的时候,将函数执行权转移进来,当异步函数执行结束时再将执行权给转移回来。因而在 generator 外部对于异步操作的形式,能够以同步的程序来书写。应用这种形式须要思考的问题是何时将函数的控制权转移回来,因而须要有一个主动执行 generator 的机制,比如说 co 模块等形式来实现 generator 的主动执行。
  • async 函数 的形式,async 函数是 generator 和 promise 实现的一个主动执行的语法糖,它外部自带执行器,当函数外部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会期待 promise 对象的状态变为 resolve 后再持续向下执行。因而能够将异步逻辑,转化为同步的程序来书写,并且这个函数能够主动执行。

实现一个三角形

CSS 绘制三角形次要用到的是 border 属性,也就是边框。

平时在给盒子设置边框时,往往都设置很窄,就可能误以为边框是由矩形组成的。实际上,border 属性是右三角形组成的,上面看一个例子:

div {
    width: 0;
    height: 0;
    border: 100px solid;
    border-color: orange blue red green;
}

将元素的长宽都设置为 0

(1)三角 1

div {width: 0;    height: 0;    border-top: 50px solid red;    border-right: 50px solid transparent;    border-left: 50px solid transparent;}

(2)三角 2

div {
    width: 0;
    height: 0;
    border-bottom: 50px solid red;
    border-right: 50px solid transparent;
    border-left: 50px solid transparent;
}

(3)三角 3

div {
    width: 0;
    height: 0;
    border-left: 50px solid red;
    border-top: 50px solid transparent;
    border-bottom: 50px solid transparent;
}

(4)三角 4

div {
    width: 0;
    height: 0;
    border-right: 50px solid red;
    border-top: 50px solid transparent;
    border-bottom: 50px solid transparent;
}

(5)三角 5

div {
    width: 0;
    height: 0;
    border-top: 100px solid red;
    border-right: 100px solid transparent;
}

还有很多,就不一一实现了,总体的准则就是通过上下左右边框来管制三角形的方向,用边框的宽度比来管制三角形的角度。

Object.is() 与比拟操作符“===”、“==”的区别?

  • 应用双等号(==)进行相等判断时,如果两边的类型不统一,则会进行强制类型转化后再进行比拟。
  • 应用三等号(===)进行相等判断时,如果两边的类型不统一时,不会做强制类型准换,间接返回 false。
  • 应用 Object.is 来进行相等判断时,个别状况下和三等号的判断雷同,它解决了一些非凡的状况,比方 -0 和 +0 不再相等,两个 NaN 是相等的。

解释性语言和编译型语言的区别

(1)解释型语言
应用专门的解释器对源程序逐行解释成特定平台的机器码并立刻执行。是代码在执行时才被解释器一行行动静翻译和执行,而不是在执行之前就实现翻译。解释型语言不须要当时编译,其间接将源代码解释成机器码并立刻执行,所以只有某一平台提供了相应的解释器即可运行该程序。其特点总结如下

  • 解释型语言每次运行都须要将源代码解释称机器码并执行,效率较低;
  • 只有平台提供相应的解释器,就能够运行源代码,所以能够不便源程序移植;
  • JavaScript、Python 等属于解释型语言。

(2)编译型语言
应用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能辨认的可执行性程序的格局。在编译型语言写的程序执行之前,须要一个专门的编译过程,把源代码编译成机器语言的文件,如 exe 格局的文件,当前要再运行时,间接应用编译后果即可,如间接运行 exe 文件。因为只需编译一次,当前运行时不须要编译,所以编译型语言执行效率高。其特点总结如下:

  • 一次性的编译成平台相干的机器语言文件,运行时脱离开发环境,运行效率高;
  • 与特定平台相干,个别无奈移植到其余平台;
  • C、C++ 等属于编译型语言。

两者次要区别在于: 前者源程序编译后即可在该平台运行,后者是在运行期间才编译。所以前者运行速度快,后者跨平台性好。

Promise.allSettled

形容 :等到所有promise 都返回后果,就返回一个 promise 实例。

实现

Promise.allSettled = function(promises) {return new Promise((resolve, reject) => {if(Array.isArray(promises)) {if(promises.length === 0) return resolve(promises);
            let result = [];
            let count = 0;
            promises.forEach((item, index) => {Promise.resolve(item).then(
                    value => {
                        count++;
                        result[index] = {
                            status: 'fulfilled',
                            value: value
                        };
                        if(count === promises.length) resolve(result);
                    }, 
                    reason => {
                        count++;
                        result[index] = {
                            status: 'rejected'.
                            reason: reason
                        };
                        if(count === promises.length) resolve(result);
                    }
                );
            });
        }
        else return reject(new TypeError("Argument is not iterable"));
    });
}

说一下数组如何去重, 你有几种办法?

let arr = [1,1,"1","1",true,true,"true",{},{},"{}",null,null,undefined,undefined]

// 办法 1
let uniqueOne = Array.from(new Set(arr)) console.log(uniqueOne)

// 办法 2
let uniqueTwo = arr => {let map = new Map(); // 或者用空对象 let obj = {} 利用对象属性不能反复得个性
    let brr = []
    arr.forEach( item => {if(!map.has(item)) {// 如果是对象得话就判断 !obj[item]
            map.set(item,true) // 如果是对象得话就 obj[item] =true 其余一样
            brr.push(item)
        }
    })
    return brr
}
console.log(uniqueTwo(arr))

// 办法 3
let uniqueThree = arr => {let brr = []
    arr.forEach(item => {
        // 应用 indexOf 返回数组是否蕴含某个值 没有就返回 -1 有就返回下标
        if(brr.indexOf(item) === -1) brr.push(item)
        // 或者应用 includes 返回数组是否蕴含某个值 没有就返回 false 有就返回 true
        if(!brr.includes(item)) brr.push(item)
    })
    return brr
}
console.log(uniqueThree(arr))

// 办法 4
let uniqueFour = arr => {                                         
     // 应用 filter 返回符合条件的汇合
    let brr = arr.filter((item,index) => {return arr.indexOf(item) === index
    })
    return brr
}
console.log(uniqueFour(arr))

POST 和 PUT 申请的区别

  • PUT 申请是向服务器端发送数据,从而批改数据的内容,然而不会减少数据的品种等,也就是说无论进行多少次 PUT 操作,其后果并没有不同。(能够了解为时 更新数据
  • POST 申请是向服务器端发送数据,该申请会扭转数据的品种等资源,它会创立新的内容。(能够了解为是 创立数据

use strict 是什么意思 ? 应用它区别是什么?

use strict 是一种 ECMAscript5 增加的(严格模式)运行模式,这种模式使得 Javascript 在更严格的条件下运行。设立严格模式的目标如下:

  • 打消 Javascript 语法的不合理、不谨严之处,缩小怪异行为;
  • 打消代码运行的不平安之处,保障代码运行的平安;
  • 进步编译器效率,减少运行速度;
  • 为将来新版本的 Javascript 做好铺垫。

区别:

  • 禁止应用 with 语句。
  • 禁止 this 关键字指向全局对象。
  • 对象不能有重名的属性。
退出移动版