判断数组的形式有哪些
- 通过 Object.prototype.toString.call()做判断
Object.prototype.toString.call(obj).slice(8,-1) === 'Array';
- 通过原型链做判断
obj.__proto__ === Array.prototype;
- 通过 ES6 的 Array.isArray()做判断
Array.isArrray(obj);
- 通过 instanceof 做判断
obj instanceof Array
- 通过 Array.prototype.isPrototypeOf
Array.prototype.isPrototypeOf(obj)
Vue 的父子组件生命周期钩子函数执行程序?
<!-- 加载渲染过程 -->
<!-- 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created ->
子 beforeMount -> 子 mounted -> 父 mounted -->
<!-- 子组件更新过程 -->
<!-- 父 beforeUpdate -> 子 beforeUpdate -> 子 updaed -> 父 updated -->
<!-- 父组件跟新过程 -->
<!-- 父 beforeUpdate -> 父 updated -->
<!-- 销毁过程 -->
<!-- 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed -->
== 操作符的强制类型转换规定?
对于 ==
来说,如果比照单方的类型 不一样 ,就会进行 类型转换。如果比照 x
和 y
是否雷同,就会进行如下判断流程:
- 首先会判断两者类型是否 雷同,雷同的话就比拟两者的大小;
- 类型不雷同的话,就会进行类型转换;
- 会先判断是否在比照
null
和undefined
,是的话就会返回true
- 判断两者类型是否为
string
和number
,是的话就会将字符串转换为number
1 == '1'
↓
1 == 1
- 判断其中一方是否为
boolean
,是的话就会把boolean
转为number
再进行判断
'1' == true
↓
'1' == 1
↓
1 == 1
- 判断其中一方是否为
object
且另一方为string
、number
或者symbol
,是的话就会把object
转为原始类型再进行判断
'1' == {name: 'js'} ↓'1' == '[object Object]'
写代码:实现函数可能深度克隆根本类型
浅克隆:
function shallowClone(obj) {let cloneObj = {};
for (let i in obj) {cloneObj[i] = obj[i];
}
return cloneObj;
}
深克隆:
- 思考根底类型
-
援用类型
- RegExp、Date、函数 不是 JSON 平安的
- 会失落 constructor,所有的构造函数都指向 Object
- 破解循环援用
function deepCopy(obj) {if (typeof obj === 'object') {var result = obj.constructor === Array ? [] : {};
for (var i in obj) {result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
}
} else {var result = obj;}
return result;
}
如何防止回流与重绘?
缩小回流与重绘的措施:
- 操作 DOM 时,尽量在低层级的 DOM 节点进行操作
- 不要应用
table
布局,一个小的改变可能会使整个table
进行从新布局 - 应用 CSS 的表达式
- 不要频繁操作元素的款式,对于动态页面,能够批改类名,而不是款式。
- 应用 absolute 或者 fixed,使元素脱离文档流,这样他们发生变化就不会影响其余元素
- 防止频繁操作 DOM,能够创立一个文档片段
documentFragment
,在它下面利用所有 DOM 操作,最初再把它增加到文档中 - 将元素先设置
display: none
,操作完结后再把它显示进去。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。 - 将 DOM 的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于 浏览器的渲染队列机制。
浏览器针对页面的回流与重绘,进行了本身的优化——渲染队列
浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了肯定的数量或者到了肯定的工夫距离,浏览器就会对队列进行批处理。这样就会让屡次的回流、重绘变成一次回流重绘。
下面,将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,本来应该是触发屡次回流,变成了只触发一次回流。
webpack 配置入口进口
module.exports={
// 入口文件的配置项
entry:{},
// 进口文件的配置项
output:{},
// 模块:例如解读 CSS, 图片如何转换,压缩
module:{},
// 插件,用于生产模版和各项性能
plugins:[],
// 配置 webpack 开发服务性能
devServer:{}}
简略形容了一下这几个属性是干什么的。形容一下 npm run dev / npm run build 执行的是哪些文件
通过配置 proxyTable 来达到开发环境跨域的问题,而后又能够扩大和他聊聊跨域的产生,如何跨域
最初能够在聊聊 webpack 的优化,例如 babel-loader 的优化,gzip 压缩等等
URL 有哪些组成部分
一个残缺的 URL 包含以下几局部:
- 协定局部:该 URL 的协定局部为“http:”,这代表网页应用的是 HTTP 协定。在 Internet 中能够应用多种协定,如 HTTP,FTP 等等本例中应用的是 HTTP 协定。在 ”HTTP” 前面的“//”为分隔符;
- 域名局部
- 端口局部:跟在域名前面的是端口,域名和端口之间应用“:”作为分隔符。端口不是一个 URL 必须的局部,如果省略端口局部,将采纳默认端口(HTTP 协定默认端口是 80,HTTPS 协定默认端口是 443);
- 虚拟目录局部:从域名后的第一个“/”开始到最初一个“/”为止,是虚拟目录局部。虚拟目录也不是一个 URL 必须的局部。本例中的虚拟目录是“/news/”;
- 文件名局部:从域名后的最初一个“/”开始到“?”为止,是文件名局部,如果没有“?”, 则是从域名后的最初一个“/”开始到“#”为止,是文件局部,如果没有“?”和“#”,那么从域名后的最初一个“/”开始到完结,都是文件名局部。本例中的文件名是“index.asp”。文件名局部也不是一个 URL 必须的局部,如果省略该局部,则应用默认的文件名;
- 锚局部:从“#”开始到最初,都是锚局部。本例中的锚局部是“name”。锚局部也不是一个 URL 必须的局部;
- 参数局部:从“?”开始到“#”为止之间的局部为参数局部,又称搜寻局部、查问局部。本例中的参数局部为“boardID=5&ID=24618&page=1”。参数能够容许有多个参数,参数与参数之间用“&”作为分隔符。
说一下你对盒模型的了解?
CSS3 中的盒模型有以下两种: 规范盒模型、IE 盒模型
盒模型都是由四个局部组成的, 别离是 margin、border、padding 和 content
规范盒模型和 IE 盒模型的区别在于设置 width 和 height 时, 所对应的范畴不同
1、规范盒模型的 width 和 height 属性的范畴只蕴含了 content
2、IE 盒模型的 width 和 height 属性的范畴蕴含了 border、padding 和 content
能够通过批改元素的 box-sizing 属性来扭转元素的盒模型;1、box-sizing:content-box 示意规范盒模型(默认值)2、box-sizing:border-box 示意 IE 盒模型(怪异盒模型)
AJAX
题目形容: 利用 XMLHttpRequest 手写 AJAX 实现
实现代码如下:
const getJSON = function (url) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {if (xhr.readyState !== 4) return;
if (xhr.status === 200 || xhr.status === 304) {resolve(xhr.responseText);
} else {reject(new Error(xhr.responseText));
}
};
xhr.send();});
};
::before 和 :after 的双冒号和单冒号有什么区别?
(1)冒号 (:
) 用于 CSS3
伪类,双冒号 (::
) 用于 CSS3
伪元素。
(2)::before
就是以一个子元素的存在,定义在元素主体内容之前的一个伪元素。并不存在于 dom
之中,只存在在页面之中。
留神: :before
和 :after
这两个伪元素,是在 CSS2.1
里新呈现的。起初,伪元素的前缀应用的是单冒号语法,但随着 Web
的进化,在 CSS3
的标准里,伪元素的语法被批改成应用双冒号,成为::before
、::after
。
代码输入问题
function Parent() {
this.a = 1;
this.b = [1, 2, this.a];
this.c = {demo: 5};
this.show = function () {console.log(this.a , this.b , this.c.demo);
}
}
function Child() {
this.a = 2;
this.change = function () {this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
Child.prototype = new Parent();
var parent = new Parent();
var child1 = new Child();
var child2 = new Child();
child1.a = 11;
child2.a = 12;
parent.show();
child1.show();
child2.show();
child1.change();
child2.change();
parent.show();
child1.show();
child2.show();
输入后果:
parent.show(); // 1 [1,2,1] 5
child1.show(); // 11 [1,2,1] 5
child2.show(); // 12 [1,2,1] 5
parent.show(); // 1 [1,2,1] 5
child1.show(); // 5 [1,2,1,11,12] 5
child2.show(); // 6 [1,2,1,11,12] 5
这道题目值得神帝,他波及到的知识点很多,例如 this 的指向、原型、原型链、类的继承、数据类型 等。
解析:
- parent.show(),能够间接取得所需的值,没啥好说的;
- child1.show(),
Child
的构造函数本来是指向Child
的,题目显式将Child
类的原型对象指向了Parent
类的一个实例,须要留神Child.prototype
指向的是Parent
的实例parent
,而不是指向Parent
这个类。 - child2.show(),这个也没啥好说的;
- parent.show(),
parent
是一个Parent
类的实例,Child.prorotype
指向的是Parent
类的另一个实例,两者在堆内存中互不影响,所以上述操作不影响parent
实例,所以输入后果不变; - child1.show(),
child1
执行了change()
办法后,产生了怎么的变动呢? - this.b.push(this.a),因为 this 的动静指向个性,this.b 会指向
Child.prototype
上的 b 数组,this.a 会指向child1
的a属性, 所以Child.prototype.b
变成了[1,2,1,11]; - this.a = this.b.length,这条语句中
this.a
和this.b
的指向与上一句统一,故后果为child1.a
变为4; - this.c.demo = this.a++,因为
child1
本身属性并没有 c 这个属性,所以此处的this.c
会指向Child.prototype.c
,this.a
值为 4,为原始类型,故赋值操作时会间接赋值,Child.prototype.c.demo
的后果为 4,而this.a
随后自增为5(4 + 1 = 5)。 child2
执行了change()
办法, 而child2
和child1
均是Child
类的实例,所以他们的原型链指向同一个原型对象Child.prototype
, 也就是同一个parent
实例,所以child2.change()
中所有影响到原型对象的语句都会影响child1
的最终输入后果。- this.b.push(this.a),因为 this 的动静指向个性,this.b 会指向
Child.prototype
上的 b 数组,this.a 会指向child2
的a属性, 所以Child.prototype.b
变成了[1,2,1,11,12]; - this.a = this.b.length,这条语句中
this.a
和this.b
的指向与上一句统一,故后果为child2.a
变为5; - this.c.demo = this.a++,因为
child2
本身属性并没有 c 这个属性,所以此处的this.c
会指向Child.prototype.c
,故执行后果为Child.prototype.c.demo
的值变为child2.a
的值 5,而child2.a
最终自增为6(5 + 1 = 6)。
代码输入后果
function runAsync(x) {
const p = new Promise(r =>
setTimeout(() => r(x, console.log(x)), 1000)
);
return p;
}
function runReject(x) {const p = new Promise((res, rej) =>
setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
);
return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log("result:", res))
.catch(err => console.log(err));
输入后果如下:
0
Error: 0
1
2
3
能够看到在 catch 捕捉到第一个谬误之后,前面的代码还不执行,不过不会再被捕捉了。
留神:all
和 race
传入的数组中如果有会抛出异样的异步工作,那么只有最先抛出的谬误会被捕捉,并且是被 then 的第二个参数或者前面的 catch 捕捉;但并不会影响数组中其它的异步工作的执行。
let、const、var 的区别
(1)块级作用域: 块作用域由 {}
包含,let 和 const 具备块级作用域,var 不存在块级作用域。块级作用域解决了 ES5 中的两个问题:
- 内层变量可能笼罩外层变量
- 用来计数的循环变量泄露为全局变量
(2)变量晋升: var 存在变量晋升,let 和 const 不存在变量晋升,即在变量只能在申明之后应用,否在会报错。
(3)给全局增加属性: 浏览器的全局对象是 window,Node 的全局对象是 global。var 申明的变量为全局变量,并且会将该变量增加为全局对象的属性,然而 let 和 const 不会。
(4)反复申明: var 申明变量时,能够反复申明变量,后申明的同名变量会笼罩之前申明的遍历。const 和 let 不容许反复申明变量。
(5)暂时性死区: 在应用 let、const 命令申明变量之前,该变量都是不可用的。这在语法上,称为 暂时性死区。应用 var 申明的变量不存在暂时性死区。
(6)初始值设置: 在变量申明时,var 和 let 能够不必设置初始值。而 const 申明变量必须设置初始值。
(7)指针指向: let 和 const 都是 ES6 新增的用于创立变量的语法。let 创立的变量是能够更改指针指向(能够从新赋值)。但 const 申明的变量是不容许扭转指针的指向。
区别 | var | let | const |
---|---|---|---|
是否有块级作用域 | × | ✔️ | ✔️ |
是否存在变量晋升 | ✔️ | × | × |
是否增加全局属性 | ✔️ | × | × |
是否反复申明变量 | ✔️ | × | × |
是否存在暂时性死区 | × | ✔️ | ✔️ |
是否必须设置初始值 | × | × | ✔️ |
是否扭转指针指向 | ✔️ | ✔️ | × |
Cookie、LocalStorage、SessionStorage 区别
浏览器端罕用的存储技术是 cookie、localStorage 和 sessionStorage。
- cookie: 其实最开始是服务器端用于记录用户状态的一种形式,由服务器设置,在客户端存储,而后每次发动同源申请时,发送给服务器端。cookie 最多能存储 4 k 数据,它的生存工夫由 expires 属性指定,并且 cookie 只能被同源的页面访问共享。
- sessionStorage: html5 提供的一种浏览器本地存储的办法,它借鉴了服务器端 session 的概念,代表的是一次会话中所保留的数据。它个别可能存储 5M 或者更大的数据,它在以后窗口敞开后就生效了,并且 sessionStorage 只能被同一个窗口的同源页面所访问共享。
- localStorage: html5 提供的一种浏览器本地存储的办法,它个别也可能存储 5M 或者更大的数据。它和 sessionStorage 不同的是,除非手动删除它,否则它不会生效,并且 localStorage 也只能被同源页面所访问共享。
下面几种形式都是存储大量数据的时候的存储形式,当须要在本地存储大量数据的时候,咱们能够应用浏览器的 indexDB 这是浏览器提供的一种本地的数据库存储机制。它不是关系型数据库,它外部采纳对象仓库的模式存储数据,它更靠近 NoSQL 数据库。
TCP 和 UDP 的应用场景
- TCP 利用场景: 效率要求绝对低,但对准确性要求绝对高的场景。因为传输中须要对数据确认、重发、排序等操作,相比之下效率没有 UDP 高。例如:文件传输(精确高要求高、然而速度能够绝对慢)、承受邮件、近程登录。
- UDP 利用场景: 效率要求绝对高,对准确性要求绝对低的场景。例如:QQ 聊天、在线视频、网络语音电话(即时通讯,速度要求高,然而呈现偶然断续不是太大问题,并且此处齐全不能够应用重发机制)、播送通信(播送、多播)。
对浏览器内核的了解
浏览器内核次要分成两局部:
- 渲染引擎的职责就是渲染,即在浏览器窗口中显示所申请的内容。默认状况下,渲染引擎能够显示 html、xml 文档及图片,它也能够借助插件显示其余类型数据,例如应用 PDF 阅读器插件,能够显示 PDF 格局。
- JS 引擎:解析和执行 javascript 来实现网页的动态效果。
最开始渲染引擎和 JS 引擎并没有辨别的很明确,起初 JS 引擎越来越独立,内核就偏向于只指渲染引擎。
new 操作符的实现原理
new 操作符的执行过程:
(1)首先创立了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象增加属性)
(4)判断函数的返回值类型,如果是值类型,返回创立的对象。如果是援用类型,就返回这个援用类型的对象。
具体实现:
function objectFactory() {
let newObject = null;
let constructor = Array.prototype.shift.call(arguments);
let result = null;
// 判断参数是否是一个函数
if (typeof constructor !== "function") {console.error("type error");
return;
}
// 新建一个空对象,对象的原型为构造函数的 prototype 对象
newObject = Object.create(constructor.prototype);
// 将 this 指向新建对象,并执行函数
result = constructor.apply(newObject, arguments);
// 判断返回对象
let flag = result && (typeof result === "object" || typeof result === "function");
// 判断返回后果
return flag ? result : newObject;
}
// 应用办法
objectFactory(构造函数, 初始化参数);
对 JSON 的了解
JSON 是一种基于文本的轻量级的数据交换格局。它能够被任何的编程语言读取和作为数据格式来传递。
在我的项目开发中,应用 JSON 作为前后端数据交换的形式。在前端通过将一个合乎 JSON 格局的数据结构序列化为
JSON 字符串,而后将它传递到后端,后端通过 JSON 格局的字符串解析后生成对应的数据结构,以此来实现前后端数据的一个传递。
因为 JSON 的语法是基于 js 的,因而很容易将 JSON 和 js 中的对象弄混,然而应该留神的是 JSON 和 js 中的对象不是一回事,JSON 中对象格局更加严格,比如说在 JSON 中属性值不能为函数,不能呈现 NaN 这样的属性值等,因而大多数的 js 对象是不合乎 JSON 对象的格局的。
在 js 中提供了两个函数来实现 js 数据结构和 JSON 格局的转换解决,
- JSON.stringify 函数,通过传入一个合乎 JSON 格局的数据结构,将其转换为一个 JSON 字符串。如果传入的数据结构不合乎 JSON 格局,那么在序列化的时候会对这些值进行对应的非凡解决,使其符合规范。在前端向后端发送数据时,能够调用这个函数将数据对象转化为 JSON 格局的字符串。
- JSON.parse() 函数,这个函数用来将 JSON 格局的字符串转换为一个 js 数据结构,如果传入的字符串不是规范的 JSON 格局的字符串的话,将会抛出谬误。当从后端接管到 JSON 格局的字符串时,能够通过这个办法来将其解析为一个 js 数据结构,以此来进行数据的拜访。
实现有并行限度的 Promise 调度器
题目形容:JS 实现一个带并发限度的异步调度器 Scheduler,保障同时运行的工作最多有两个
addTask(1000,"1");
addTask(500,"2");
addTask(300,"3");
addTask(400,"4");
的输入程序是:2 3 1 4
整个的残缺执行流程:一开始 1、2 两个工作开始执行
500ms 时,2 工作执行结束,输入 2,工作 3 开始执行
800ms 时,3 工作执行结束,输入 3,工作 4 开始执行
1000ms 时,1 工作执行结束,输入 1,此时只剩下 4 工作在执行
1200ms 时,4 工作执行结束,输入 4
实现代码如下:
class Scheduler {constructor(limit) {this.queue = [];
this.maxCount = limit;
this.runCounts = 0;
}
add(time, order) {const promiseCreator = () => {return new Promise((resolve, reject) => {setTimeout(() => {console.log(order);
resolve();}, time);
});
};
this.queue.push(promiseCreator);
}
taskStart() {for (let i = 0; i < this.maxCount; i++) {this.request();
}
}
request() {if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {return;}
this.runCounts++;
this.queue
.shift()()
.then(() => {
this.runCounts--;
this.request();});
}
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();
TCP/IP 五层协定
TCP/IP
五层协定和 OSI
的七层协定对应关系如下:
- 应用层 (application layer):间接为利用过程提供服务。应用层协定定义的是利用过程间通信和交互的规定,不同的利用有着不同的应用层协定,如 HTTP 协定(万维网服务)、FTP 协定(文件传输)、SMTP 协定(电子邮件)、DNS(域名查问)等。
-
传输层 (transport layer):有时也译为运输层,它负责为两台主机中的过程提供通信服务。该层次要有以下两种协定:
- 传输控制协议 (Transmission Control Protocol,TCP):提供面向连贯的、牢靠的数据传输服务,数据传输的根本单位是报文段(segment);
- 用户数据报协定 (User Datagram Protocol,UDP):提供无连贯的、尽最大致力的数据传输服务,但不保障数据传输的可靠性,数据传输的根本单位是用户数据报。
- 网络层 (internet layer):有时也译为网际层,它负责为两台主机提供通信服务,并通过抉择适合的路由将数据传递到指标主机。
- 数据链路层 (data link layer):负责将网络层交下来的 IP 数据报封装成帧,并在链路的两个相邻节点间传送帧,每一帧都蕴含数据和必要的管制信息(如同步信息、地址信息、差错控制等)。
- 物理层 (physical Layer):确保数据能够在各种物理媒介上进行传输,为数据的传输提供牢靠的环境。
从上图中能够看出,TCP/IP
模型比 OSI
模型更加简洁,它把 应用层 / 表示层 / 会话层
全副整合为了 应用层
。
在每一层都工作着不同的设施,比方咱们罕用的交换机就工作在数据链路层的,个别的路由器是工作在网络层的。在每一层实现的协定也各不同,即每一层的服务也不同,下图列出了每层次要的传输协定:
同样,TCP/IP
五层协定的通信形式也是对等通信: