前端面试题-JS(六)

86 谈谈变量提升?

当执⾏ JS 代码时,会⽣成执⾏环境,只要代码不是写在函数中的,就是在全局执⾏环境中,函数中的代码会产⽣函数执⾏环
境,只此两种执⾏环境
  • 接下来让我们看⼀个⽼⽣常谈的例⼦, var
b() // call b
console.log(a) // undefined
var a = 'Hello world'
function b() {
console.log('call b')
}

变量提升

这是因为函数和变量提升的原因。通常提升的解释是说将声明的代码移动到了顶部,这其实没有什么错误,便于⼤家理解。但是更准确
的解释应该是:在⽣成执⾏环境时,会有两个阶段。第⼀个阶段是创建的阶段,JS 解释器会找出需要提升的变量和函
数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存⼊内存中,变量只声明并且赋值为 undefined ,所以在第⼆个阶
段,也就是代码执⾏阶段,我们可以直接提前使⽤

在提升的过程中,相同的函数会覆盖上⼀个函数,并且函数优先于变量提升

b() // call b second
function b() {
console.log('call b fist')
}
function b() {
console.log('call b second')
}
var b = 'Hello world'
复制代码 var 会产⽣很多错误,所以在 ES6 中引⼊了 let 。 let 不能在声明前使⽤,但是这并不是常说的 let 不会提升,
 let 提升了,在第⼀阶段内存也已经为他开辟好了空间,但是因为这个声明的特性导致了并不能在声明前使⽤

87 什么是单线程,和异步的关系

  • 单线程 - 只有⼀个线程,只能做⼀件事

  • 原因 - 避免 DOM 渲染的冲突

    • 浏览器需要渲染 DOM
    • JS 可以修改 DOM 结构
    • JS 执⾏的时候,浏览器 DOM 渲染会暂停
    • 两段 JS 也不能同时执⾏(都修改 DOM 就冲突了)
    • webworker ⽀持多线程,但是不能访问 DOM
  • 解决⽅案 - 异步

92 实现效果,点击容器内的图标,图标边框变成border 1px solidred,点击空⽩处重置

const box = document.getElementById('box');
function isIcon(target) {
return target.className.includes('icon');
}
box.onClick = function(e) {
e.stopPropagation();
const target = e.target;
if (isIcon(target)) {
target.style.border = '1px solid red';
}
}
const doc = document;
doc.onclick = function(e) {
const children = box.children;
for(let i; i < children.length; i++) {
if (isIcon(children[i])) {
children[i].style.border = 'none';
}
}
}

93 请简单实现双向数据绑定 mvvm

<input id="input"/>
const data = {};
const input = document.getElementById('input');
Object.defineProperty(data, 'text', {
set(value) {
input.value = value;
this.value = value;
}
});
input.onChange = function(e) {
data.text = e.target.value;
}

94 实现Storage,使得该对象为单例,并对 localStorage 进⾏封装设置值setItem(key,value)和getItem(key)



var instance = null;
class Storage {
static getInstance() {
if (!instance) {
instance = new Storage();
}
return instance;

95 说说 event loop

⾸先, js 是单线程的,主要的任务是处理⽤户的交互,⽽⽤户的交互⽆⾮就是响应 DOM 的增删改,使⽤事件队列的形式,⼀次事件
循环只处理⼀个事件响应,使得脚本执⾏相对连续,所以有了事件队列,⽤来储存待执⾏的事件,那么事件队列的事件从哪⾥被 push
 进来的呢。那就是另外⼀个线程叫事件触发线程做的事情了,他的作⽤主要是在定时触发器线程、异步 HTTP 请求线程满⾜特定条件
 下的回调函数 push 到事件队列中,等待 js 引擎空闲的时候去执⾏,当然js引擎执⾏过程中有优先级之分,⾸先js引擎在⼀次事
 件循环中,会先执⾏js线程的主任务,然后会去查找是否有微任务microtask(promise) ,如果有那就优先执⾏微任务,如果没有
 ,在去查找宏任务 macrotask(setTimeout、setInterval) 进⾏执⾏

96 说说事件流

事件流分为两种,捕获事件流和冒泡事件流

  • 捕获事件流从根节点开始执⾏,⼀直往⼦节点查找执⾏,直到查找执⾏到⽬标节点

  • 冒泡事件流从⽬标节点开始执⾏,⼀直往⽗节点冒泡查找执⾏,直到查到到根节点

    事件流分为三个阶段,⼀个是捕获节点,⼀个是处于⽬标节点阶段,⼀个是冒泡阶段
    

100 说说从输⼊URL到看到⻚⾯发⽣的全过程,越详细越好

  • ⾸先浏览器主进程接管,开了⼀个下载线程。
  • 然后进⾏HTTP请求(DNS查询、IP寻址等等),中间会有三次捂⼿,等待响应,开始下载响应报⽂。
  • 将下载完的内容转交给Renderer进程管理。
  • Renderer进程开始解析css rule tree和dom tree,这两个过程是并⾏的,所以⼀般我会把link标签放在⻚⾯顶部。
  • 解析绘制过程中,当浏览器遇到link标签或者script、img等标签,浏览器会去下载这些内容,遇到时候缓存的使⽤缓存,不适⽤缓存的重新下载资源。
  • css rule tree和dom tree⽣成完了之后,开始合成render tree,这个时候浏览器会进⾏layout,开始计算每⼀个节点的位置,然后进⾏绘制。
  • 绘制结束后,关闭TCP连接,过程有四次挥⼿

101 描述⼀下 this

this ,函数执⾏的上下⽂,可以通过 apply , call , bind 改变 this的指向。对于匿名函数或者直接调⽤的函数来说,
this指向全局上下⽂(浏览器为window,NodeJS为 global ),剩下的函数调⽤,那就是谁调⽤它,this 就指向谁。当然还
有es6的箭头函数,箭头函数的指向取决于该箭头函数声明的位置,在哪⾥声明, this 就指向哪⾥

102 说⼀下浏览器的缓存机制

浏览器缓存机制有两种,⼀种为强缓存,⼀种为协商缓存
  • 对于强缓存,浏览器在第⼀次请求的时候,会直接下载资源,然后缓存在本地,第⼆次请求的时候,直接使⽤缓存。
  • 对于协商缓存,第⼀次请求缓存且保存缓存标识与时间,重复请求向服务器发送缓存标识
    和最后缓存时间,服务端进⾏校验,如果失效则使⽤缓存

协商缓存相关设置

  • Exprires :服务端的响应头,第⼀次请求的时候,告诉客户端,该资源什么时候会过期。 Exprires 的缺陷是必须保证服务端时间和客户端时间严格同步。
  • Cache-control:max-age :表示该资源多少时间后过期,解决了客户端和服务端时间必须同步的问题,
  • If-None-Match/ETag :缓存标识,对⽐缓存时使⽤它来标识⼀个缓存,第⼀次请求的时候,服务端会返回该标识给客户端,客户端在第⼆次请求的时候会带上该标识与服务端进⾏对⽐并返回 If-None-Match 标识是否表示匹配。
  • Last-modified/If-Modified-Since :第⼀次请求的时候服务端返回 Last-modified表明请求的资源上次的修改时间,第⼆次请求的时候客户端带上请求头 If-Modified-Since ,表示资源上次的修改时间,服务端拿到这两个字段进⾏对⽐

103 现在要你完成⼀个Dialog组件,说说你设计的思路?它应该有什么功能?

  • 该组件需要提供 hook 指定渲染位置,默认渲染在body下⾯。
  • 然后改组件可以指定外层样式,如宽度等
  • 组件外层还需要⼀层 mask 来遮住底层内容,点击 mask 可以执⾏传进来的 onCancel 函数关闭 Dialog 。
  • 另外组件是可控的,需要外层传⼊ visible 表示是否可⻅。
  • 然后 Dialog 可能需要⾃定义头head和底部 footer ,默认有头部和底部,底部有⼀个确
  • 认按钮和取消按钮,确认按钮会执⾏外部传进来的 onOk 事件,然后取消按钮会执⾏外部传进来的 onCancel 事件。
  • 当组件的 visible 为 true 时候,设置 body 的 overflow 为 hidden ,隐藏 body 的滚动条,反之显示滚动条。
  • 组件⾼度可能⼤于⻚⾯⾼度,组件内部需要滚动条。
  • 只有组件的 visible 有变化且为 ture 时候,才重渲染组件内的所有内容

104 caller 和 callee 的区别

callee

caller 返回⼀个函数的引⽤,这个函数调⽤了当前的函数。

使⽤这个属性要注意

  • 这个属性只有当函数在执⾏时才有⽤

  • 如果在 javascript 程序中,函数是由顶层调⽤的,则返回 null

    functionName.caller: functionName 是当前正在执⾏的函数。
    
function a() {
console.log(a.caller)
}

callee

callee 放回正在执⾏的函数本身的引⽤,它是 arguments 的⼀个属性
使⽤callee时要注意:
  • 这个属性只有在函数执⾏时才有效
  • 它有⼀个 length 属性,可以⽤来获得形参的个数,因此可以⽤来⽐较形参和实参个数是否⼀致,即⽐较 arguments.length 是否等于 arguments.callee.length
  • 它可以⽤来递归匿名函数。
function a() {
console.log(arguments.callee)
}

105 ajax、axios、fetch区别

jQuery ajax

$.ajax({
type: 'POST',
url: url,
data: data,
dataType: dataType,
success: function () {},
error: function () {}
});
优缺点:
  • 本身是针对 MVC 的编程,不符合现在前端 MVVM 的浪潮
  • 基于原⽣的 XHR 开发, XHR 本身的架构不清晰,已经有了 fetch 的替代⽅案
  • JQuery 整个项⽬太⼤,单纯使⽤ ajax 却要引⼊整个 JQuery ⾮常的不合理(采取个性化打包的⽅案⼜不能享受CDN服务)

axios

axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
优缺点:
  • 从浏览器中创建 XMLHttpRequest
  • 从 node.js 发出 http 请求
  • ⽀持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • ⾃动转换 JSON 数据
  • 客户端⽀持防⽌ CSRF/XSRF

fetch

try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch(e) {
console.log("Oops, error", e)
}
优缺点:
  • fetcht 只对⽹络请求报错,对 400 , 500 都当做成功的请求,需要封装去处理
  • fetch 默认不会带 cookie ,需要添加配置项
  • fetch 不⽀持 abort ,不⽀持超时控制,使⽤ setTimeout 及 Promise.reject 的实现的超时控制并不能阻⽌请求过程继续在后台运⾏,造成了量的浪费
  • fetch 没有办法原⽣监测请求的进度,⽽XHR可以

你可能感兴趣的:(前端,javascript,前端,开发语言)