赞
踩
前端面试+学习笔记(HTML+CSS+JavaScript+ES6+Vue+NodeJs)
是什么:每个元素被表示为一个矩形的盒子,有四个部分组成:内容(content)、内边距(padding)、边框(border)、外边距(margin)。它在页面中所占的实际大小(宽高)是content+padding+border+margin之和。
盒模型有两种:标准盒模型(W3C盒模型)、IE盒模型。
两种盒模型的区别:标准盒模型内容大小就是content大小、而IE盒模型内容大小则是content+padding+border总的大小。
**怎么设置两种盒模型:**通过设置box-sizing属性为content-box(默认值:标准盒模型)、border-box(IE盒模型)。
box-sizing使用场景:若设置子元素的margin或border时可能会撑破父元素的尺寸,就需要使用box-sizing:border-box来将border包含进元素的尺寸中。
页面被加载时,link会同时被加载;而@import引用的CSS会等到页面被加载完成后再加载
link是XHTML标签,没有兼容问题;而@import只有在IE5以上才能被识别
link支持使用JavaScript控制DOM修改样式;而@import不支持。
Trident内核:(国产的绝大部分浏览器)IE,360,搜狗
**Gecko内核:**Firefox,NetScape6及以上
**Presto内核:**Opera7及以上
Webkit内核:(国产大部分双核浏览器其中一核)Safari(苹果),Chrome
浏览器内核:主要分成两部分:渲染引擎和JS引擎。最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎
**渲染引擎:**负责取得网页内容(HTML,XML,图像等)、整理讯息(加入CSS等),以及计算网页的显示方式,后会输出至显示器或打印机。
JS引擎:解析和执行JavaScript来实现网页的动态效果。
严格模式下,排版和JS以浏览器支持的最高标准运行;**混杂模式下,**页面以宽松向后兼容的方式显示
**如何触发混杂模式:**DOCTYPE不存在或格式不正确,会导致文档以混合模式呈现
**标准模式(standards mode)**是指浏览器按照W3C标准解析执行代码;**怪异模式(quirks mode)**则是使用浏览器自己的方式解析执行代码。
浏览器解析时到底使用何种模式,与网页中的DTD声明(文档类型定义,DOCTYPE相关)有关,忽略DTD声明,将使网页进入怪异模式。
渐进增强(progressive enhancement):针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
优雅降级(graceful degradation):一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
区别:
渐进增强观点认为应该关注于内容本身,这使得渐进增强成为一种更为合理的设计范例;优雅降级观点认为应该针对那些最高级、最完善的浏览器来设计网站。
用正确的标签做正确的事情
HTML语义化让页面的内容结构化,结构更清晰,便于对浏览器、搜索引擎解析
即使在没有样式CSS情况下也以一种文档格式显示,并且是易于阅读的
搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重,利于SEO
使阅读源代码的人更容易将网站分块,便于阅读维护理解
**选择符:**id(ID);class(类);element(标签);element element(后代);element>element(子);
element,element(群组);element+element(相邻同胞);伪类(:link,:visited,:active,:hover,:focus:first-child,:lang(language));
伪元素(:first-letter,:first-line,:before,:after);属性选择器
**可继承的选择符:**主要是文本方面的可继承,盒模型相关的属性基本没有继承特性。font-size,font-family,color,ul
**不可继承的选择符:**border,padding,margin,width,height
**优先级:**同权重下样式定义最近者高。!important>内联样式 即定义在HTML标签内的样式,(1000)>id(100)>class/伪类/属性(10)>伪元素/element(1)
**CSS引入伪类和伪元素的原因:**用来修饰DOM树以外的部分。
**是什么:**相邻的两个或多个普通流中的块元素,如果它们设置了外边距,那么在垂直方向上,外边距会发生重叠,以绝对值大的那个为最终结果显示在页面上,即最终的外边距等于发生层叠的外边距中绝对值较大者。
**最终外边距:**margin全为正(取最大值)、margin全为负(取绝对值最大的负数)、margin有正有负(分别取正数最大值a,负数的最大绝对值b,a-b)
**外边距重叠的应用:**几个段落一起布局,第一个段落的上外边距正常显示,下外边距与第二个段落的上外边距重叠。
防止外边距重叠:创建BFC元素。
不会发生外边距重叠的情况:行内元素、浮动元素、绝对定位元素之间的外边距都不会叠加。
**是什么:**决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。简言之,就是一个特殊的块,内部的元素和外部的元素不会相互影响。BFC内的盒子会在垂直方向上一个接一个地放置,垂直方向上也会发生外边距重叠。
**应用场景:**自适应布局(BFC不与float box重叠)、清除浮动(计算BFC的高度时,内部的浮动元素也被计算在内)、防止外边距重叠
如何触发BFC:float属性(不为none)、overflow属性(不为visible)、position属性(absolute,fixed)、display属性(inline-block,table-cell,table-caption,flex,inline-flex)。
**定义:**规定元素的定位类型。
**正常文档流:**指的是没有用CSS样式去控制的HTML文档结构,代码的顺序就是网页展示的顺序。
**脱离文档流:**指的是元素所显示的位置和文档代码不一致。
**static:**默认值。没有定位,元素出现在正常的文档流中。
**relative:**生成相对定位的元素,相对于其在正常文档流中的位置进行定位(不脱离文档流)。
**absolute:**生成绝对定位的元素,相对于static定位以外的最近父级元素进行定位,即相对于其直接父级元素(脱离文档流)。absolute较少直接单独使用在正常的文档流中,主要运行于进行了相对定位的元素框架层里面,相对该层的左上点进行偏移。
**fixed:**生成固定定位元素,相对于浏览器窗口进行定位。
**inherit:**从父元素继承position属性的值。
**z-index属性:**使用了relative、absolute、fixed三种定位后,都会使正常的文档流发生一定程度的改变,造成元素出现重叠的情形。为了能让重叠的元素有序的显示出来,需要在定位的相关元素加上z-index属性。其值是一个整数值,默认为0,数值越大表示拥有的优先级越高,该属性只对使用了定位的元素有效。
**定义:**规定元素应该生成的框的类型
常用属性值:
**inline:**默认值。元素会被显示为内联元素。
**none:**元素不会被显示。
**block:**元素将显示为块级元素。
**inline-block:**行内块元素,即元素像行内元素一样显示,内容像块元素一样显示。
**list-item:**元素像块元素一样显示,并添加样式列表标记。
**table:**元素会作为块级表格来显示。
**table-caption:**元素会作为一个表格标题显示。
**inherit:**从父元素继承display属性。
display属性值inline和block的区别:
block元素会独占一行,默认情况下,block元素宽度自动填满父级元素的宽度;
block元素可以设置width、height属性,即使设置了宽度,仍然是独占一行;
block元素可以设置margin和padding属性;
inline元素不会独占一行,多个相邻的行内元素会排列在同一行里面,其宽度随元素的内容而变化;
inline元素设置width、height无效;
inline元素的margin和padding属性在水平方向上能产生边距效果,垂直方向不会产生边距效果。
display:inline-block元素显示间隙
inline-block水平呈现的元素之间,HTML元素标签换行显示或标签之间有空格的情况下会有间距
消除办法:移除标签之间的空格;
使用margin-left或margin-right取负值;
对父元素设置font-size为0,然后对元素的font-size初始化;
对父元素设置letter-spacing(字符间距)为负值,然后设置元素的letter-spacing为0;
对父元素设置word-spacing(单词间距)为负值,然后设置元素的word-spacing为0。
**定义:**规定当内容溢出元素框时发生的事情
**visible:**默认值。内容不会被修剪,会呈现在元素框之外
**hidden:**内容会被修剪,并且其余内容不可见
**scroll:**内容被修剪,但浏览器会显示滚动条以便查看其余内容
**auto:**如果内容被修剪,则浏览器会显示滚动条以便查看其余内容
inherit:从父元素继承overflow属性的值
**为什么要初始化CSS样式:**因为浏览器的兼容问题,不同浏览器对有些标签的默认值时不同的,如果没有对CSS初始化往往会出现浏览器之间页面显示差异。
最简单的方法:*{margin:0;padding:0;}
**初始化CSS的缺点:**对SEO(搜索引擎优化)有一定的影响。
SEO:Search Engine Optimization,搜索引擎的优化。SEO具体是指通过网站结构调整、网站内容建设、网站代码优化以及站外优化,使网站满足搜索引擎的收录排名需求,提高网站在搜索引擎中关键字的排名,从而吸引精准用户进入网站,获得免费流量,产生直接销售或品牌推广。
**什么是CSS Hack:**一般来说针对不同的浏览器写不同的CSS,就是CSS Hack。
cursor属性规定要显示的鼠标的光标类型。
常用取值:pointer(手),crosshair(十字线),default(箭头),auto(浏览器设置的光标)
绘画canvas(通过脚本实现绘画)
用于媒介回放的video和audio元素
本地离线存储localStorage、sessionStorage
语义化更好的内容元素:article、footer、header、nav、section
表单元素:datalist(规定输入域的选项列表)、output(用于不同元素的输出)、keygen(提供一种验证用户的可靠方法)
input类型:color、date、month、week、number、email(检测是否为一个email格式的地址)、range(滑动条)、search、url、tel(输入电话号码,-time选择时间)
新标签被当做错误处理并被忽略,在DOM构建时会当做这个标签不存在
新标签被当做错误处理,在DOM构建时,这个新标签会被构造成行内元素
新标签被识别成HTML5标签,然后用DOM节点对齐进行替换
**实现标签被识别。**通过document.createElement(tagName)即可让浏览器识别新标签,浏览器支持新标签后,还可以为其添加CSS样式
JavaScript解决方案:
<!--[if It IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!--[if It IE 6]>
<script src="http://letskillie6.googlecode.com/svn/trunk/letskillie6.an_CN.pack.js"></script>
<![endif]-->
DOCTYPE声明
新增的元素
**纯表现的元素:**big,center,font,strike(删除线),u(下划线),s(删除线)
**对可用性产生负面影响的元素:**frame,frameset,noframes
会阻塞主页面的onload事件
搜索引擎的检索程序无法解读这种页面,不利于SEO
cookie是网站为了标识用户身份而存储在用户本地终端上的数据(通常经过加密),数据始终在同源的http请求中携带,即在浏览器和服务器之间来回传递;localStorage不会自动把数据发给服务器,尽在本地保存
cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储大小也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据(如会话标识);localStorage也有存储大小的限制,但比cookie大很多,可以达到5M或更大。
cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭;localStorage始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;sessionStorage在当前浏览器窗口关闭之后自动删除
localStorage支持**事件通知机制,**可以将数据更新的通知发送给监听者,API接口使用更方便;cookie的原生接口不友好,需要程序员自己封装
**localStorage如何删除数据:**localStorage对象可以将数据长期保存在客户端,除非人为清除,提供了以下几个方法:
**存储:**localStorage.setItem(key,value) 如果key存在,更新value
**获取:**localStorage.getItem(key) 如果key不存在,则返回null
**删除:**localStorage.removeItem(key) 一旦删除,key对应的数据将会全部删除
全部清除:localStorage.clear()使用removeItem逐个删除太麻烦,可以使用**clear,**执行的结果是清除所有的localStorage对象保存的数据
localStorage存储的数据是不能跨浏览器共用的,一个浏览器只能读取各自浏览器的数据。
边框:border-radius(圆角)、box-shadow(阴影)、border-image(边框图片)
背景:background-size(背景图片的尺寸)、background-origin(背景图片的定位区域)
文本效果:text-shadow(文本阴影)、word-wrap(文本换行)
转换和变形:transform(包括2D,3D转换,rotate(angle),translate(x,y),scale(x,y))
过渡:transition
动画:animation
多列:column-count(元素被分隔的列数)、column-gap(列之间的间隔)、column-rule(洌之间的宽度,样式,颜色规则)
用户界面:resize(规定是否可由用户调整元素尺寸)、box-sizing(以确切的方式适应某个区域的具体内容)、outline-offset(对轮廓进行偏移)
element:before(在元素之前添加内容) element:after(在元素之后添加内容)
element:first-of-type、element:last-of-type、element:only-of-type、element:only-child、element:nth-child(n)(第n个)
:checked、:disabled、:enabled
**问题描述:**超链接访问后hover样式就不出现了,被点击访问过的超链接样式不再具有hover和active
**解决办法:**爱恨原则LoVe/HAte。改变CSS属性的排列顺序,L-V-H-A 即a:link{} a:visited() a:hover{} a:active{}
rgba和opacity都能实现透明效果,但最大的不同在于opacity作用于元素本身以及元素内的所有内容,而rgba只作用于元素本身,子元素不会继承透明效果。
**rgba是CSS3的属性,**用法说明:rgba(R,G,B,A),参数说明R(红色值。正整数|百分数),G(绿色值。正整数|百分数),B(蓝色值。正整数|百分比),A(Alpha透明度。0(透明)~1)。IE6-8不支持rgba模式,可以使用IE滤镜处理:
父级div定义height
结尾处加空div标签,样式clear:both
父级div定义伪类:after和zoom
父级div定义overflow:hidden(同时还要定义width或zoom:1,不能定义height)
父级div定义overflow:auto(同时还要定义width或zoom:1,不能定义height)
父级也浮动,需要定义width(不推荐)
父级div定义display:table(不推荐)
结尾处加br标签,样式clear:both(父元素div定义zoom:1,不推荐)
none:(默认值)。允许浮动元素
left:在左侧不允许浮动元素
right:在右侧不允许浮动元素
both:在左右侧均不允许浮动元素
inherit:从父元素继承clear属性
normal:(默认值),使用对象的实际尺寸
:用浮点数来定义缩放比例,不允许负值
:用百分比来定义缩放比例,不允许负值
浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排
重绘是一个元素外观的改变所触发的浏览器行为(例如改变visibility,outline,background等属性),浏览器会根据元素的新属性重新绘制,是元素呈现新的外观。
**重排时更明显的一种改变,可以理解为渲染树需要重新计算。**常见的触发重排的操作:
DOM元素的几何属性变化
DOM树的结构变化(例如节点的增减、移动)
获取某些属性(例如offsetTop,offsetLeft,offsetHeight,offsetWidth,clientWidth,clientHeight等)
改变元素的一些样式(例如调整浏览器窗口大小)
重绘不会带来重新布局,并不一定伴随着重排。
在实践中,应该尽量减少重排次数和缩小重排的影响范围。有以下几种方法:
将多次改变样式属性的操作合并成一次操作
将需要多次重排的元素,position属性设为absolute或fixed,使其脱离文档流,这样它的变化就不会影响到其他元素
在内存中多次操作节点,完成后再添加到文档中去
如果要对一个元素进行复杂的操作,可以将其display属性设置为none使其隐藏,待操作完成后再显示
在需要经常获取那些引起浏览器重排的属性值时,要缓存到变量
在HTML代码中输入
对于单行代码,使用标签<code>代码</code>
对于多行代码,使用标签<pre></pre> (被包围在pre元素中的文本通常会保留空格和换行符)
26.使用mailto在网页中链接Email地址?
(1)a标签有一个作用是可以链接Email地址,使用mailto能让访问者便捷想网站管理者发送电子邮件
(2)如果mailto后面同时又多个参数的话,第一个参数必须以?开头,后面的参数每一个都以&分隔
答: ES6是新一代的JS语言标准,对分JS语言核心内容做了升级优化,规范了JS使用标准,新增了JS原生方法,使得JS使用更加规范,更加优雅,更适合大型应用的开发。学习ES6是成为专业前端正规军的必经之路。不学习ES6也可以写代码打鬼子,但是最多只能当个游击队长。
答: ES2015特指在2015年发布的新一代JS语言标准,ES6泛指下一代JS语言标准,包含ES2015、ES2016、ES2017、ES2018等。现阶段在绝大部分场景下,ES2015默认等同ES6。ES5泛指上一代语言标准。ES2015可以理解为ES5和ES6的时间分界线。
答:babel是一个 ES6 转码器,可以将 ES6 代码转为 ES5 代码,以便兼容那些还没支持ES6的平台。
答: 在ES6之前,声明变量只能用var,var方式声明变量其实是很不合理的,准确的说,是因为ES5里面没有块级作用域是很不合理的,甚至可以说是一个语言层面的bug(这也是很多c++、java开发人员看不懂,也瞧不起JS语言的劣势之一)。没有块级作用域回来带很多难以理解的问题,比如for循环var变量泄露,变量覆盖等问题。let 声明的变量拥有自己的块级作用域,且修复了var声明变量带来的变量提升问题。
答:
ES6新增了字符串模板,在拼接大段字符串时,用反斜杠(`)取代以往的字符串相加的形式,能保留所有空格和换行,使得字符串拼接看起来更加直观,更加优雅。
ES6在String原型上新增了includes()方法,用于取代传统的只能用indexOf查找包含字符的方法(indexOf返回-1表示没查到不如includes方法返回false更明确,语义更清晰), 此外还新增了startsWith(), endsWith(), padStart(),padEnd(),repeat()等方法,可方便的用于查找,补全字符串。
答:
a. 数组解构赋值。ES6可以直接以let [a,b,c] = [1,2,3]
形式进行变量赋值,在声明较多变量时,不用再写很多let(var),且映射关系清晰,且支持赋默认值。
b. 扩展运算符。ES6新增的扩展运算符(…)(重要),可以轻松的实现数组和松散序列的相互转化,可以取代arguments对象和apply方法,轻松获取未知参数个数情况下的参数集合。(尤其是在ES5中,arguments并不是一个真正的数组,而是一个类数组的对象,但是扩展运算符的逆运算却可以返回一个真正的数组)。扩展运算符还可以轻松方便的实现数组的复制和解构赋值(let a = [2,3,4]; let b = [...a]
)。
ES6在Array原型上新增了find()方法,用于取代传统的只能用indexOf查找包含数组项目的方法,且修复了indexOf查找不到NaN的bug([NaN].indexOf(NaN) === -1
).此外还新增了copyWithin(), includes(), fill(),flat()等方法,可方便的用于字符串的查找,补全,转换等。
答:
ES6在Number原型上新增了isFinite(), isNaN()方法,用来取代传统的全局isFinite(), isNaN()方法检测数值是否有限、是否是NaN。ES5的isFinite(), isNaN()方法都会先将非数值类型的参数转化为Number类型再做判断,这其实是不合理的,最造成isNaN('NaN') === true
的奇怪行为–'NaN’是一个字符串,但是isNaN却说这就是NaN。而Number.isFinite()和Number.isNaN()则不会有此类问题(Number.isNaN('NaN') === false
)。(isFinite()同上)
ES6在Math对象上新增了Math.cbrt(),trunc(),hypot()等等较多的科学计数法运算方法,可以更加全面的进行立方根、求和立方根等等科学计算。
答:
a. 对象属性变量式声明。ES6可以直接以变量形式声明对象属性或者方法,。比传统的键值对形式声明更加简洁,更加方便,语义更加清晰。
let [apple, orange] = ['red appe', 'yellow orange'];
let myFruits = {apple, orange}; // let myFruits = {apple: 'red appe', orange: 'yellow orange'};
复制代码
尤其在对象解构赋值(见优化部分b.)或者模块输出变量时,这种写法的好处体现的最为明显:
let {keys, values, entries} = Object;
let MyOwnMethods = {keys, values, entries}; // let MyOwnMethods = {keys: keys, values: values, entries: entries}
复制代码
可以看到属性变量式声明属性看起来更加简洁明了。方法也可以采用简洁写法:
let es5Fun = {
method: function(){}
};
let es6Fun = {
method(){}
}
复制代码
b. 对象的解构赋值。 ES6对象也可以像数组解构赋值那样,进行变量的解构赋值:
let {apple, orange} = {apple: 'red appe', orange: 'yellow orange'};
复制代码
c. 对象的扩展运算符(…)。 ES6对象的扩展运算符和数组扩展运算符用法本质上差别不大,毕竟数组也就是特殊的对象。对象的扩展运算符一个最常用也最好用的用处就在于可以轻松的取出一个目标对象内部全部或者部分的可遍历属性,从而进行对象的合并和分解。
let {apple, orange, ...otherFruits} = {apple: 'red apple', orange: 'yellow orange', grape: 'purple grape', peach: 'sweet peach'};
// otherFruits {grape: 'purple grape', peach: 'sweet peach'}
// 注意: 对象的扩展运算符用在解构赋值时,扩展运算符只能用在最有一个参数(otherFruits后面不能再跟其他参数)
let moreFruits = {watermelon: 'nice watermelon'};
let allFruits = {apple, orange, ...otherFruits, ...moreFruits};
复制代码
d. super 关键字。ES6在Class类里新增了类似this的关键字super。同this总是指向当前函数所在的对象不同,super关键字总是指向当前函数所在对象的原型对象。
a. ES6在Object原型上新增了is()方法,做两个目标对象的相等比较,用来完善’=‘方法。’='方法中NaN === NaN //false
其实是不合理的,Object.is修复了这个小bug。(Object.is(NaN, NaN) // true
)
b. ES6在Object原型上新增了assign()方法,用于对象新增属性或者多个对象合并。
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
复制代码
注意: assign合并的对象target只能合并source1、source2中的自身属性,并不会合并source1、source2中的继承属性,也不会合并不可枚举的属性,且无法正确复制get和set属性(会直接执行get/set函数,取return的值)。
c. ES6在Object原型上新增了getOwnPropertyDescriptors()方法,此方法增强了ES5中getOwnPropertyDescriptor()方法,可以获取指定对象所有自身属性的描述对象。结合defineProperties()方法,可以完美复制对象,包括复制get和set属性。
d. ES6在Object原型上新增了getPrototypeOf()和setPrototypeOf()方法,用来获取或设置当前对象的prototype对象。这个方法存在的意义在于,ES5中获取设置prototype对像是通过__proto__属性来实现的,然而__proto__属性并不是ES规范中的明文规定的属性,只是浏览器各大产商“私自”加上去的属性,只不过因为适用范围广而被默认使用了,再非浏览器环境中并不一定就可以使用,所以为了稳妥起见,获取或设置当前对象的prototype对象时,都应该采用ES6新增的标准用法。
d. ES6在Object原型上还新增了Object.keys(),Object.values(),Object.entries()方法,用来获取对象的所有键、所有值和所有键值对数组。
答:
a. 箭头函数**(核心)**。箭头函数是ES6核心的升级项之一,箭头函数里没有自己的this,这改变了以往JS函数中最让人难以理解的this运行机制。主要优化点:
Ⅰ. 箭头函数内的this指向的是函数定义时所在的对象,而不是函数执行时所在的对象。ES5函数里的this总是指向函数执行时所在的对象,这使得在很多情况下this的指向变得很难理解,尤其是非严格模式情况下,this有时候会指向全局对象,这甚至也可以归结为语言层面的bug之一。ES6的箭头函数优化了这一点,它的内部没有自己的this,这也就导致了this总是指向上一层的this,如果上一层还是箭头函数,则继续向上指,直到指向到有自己this的函数为止,并作为自己的this。
Ⅱ. 箭头函数不能用作构造函数,因为它没有自己的this,无法实例化。
Ⅲ. 也是因为箭头函数没有自己的this,所以箭头函数 内也不存在arguments对象。(可以用扩展运算符代替)
b. 函数默认赋值。ES6之前,函数的形参是无法给默认值得,只能在函数内部通过变通方法实现。ES6以更简洁更明确的方式进行函数默认赋值。
function es6Fuc (x, y = 'default') {
console.log(x, y);
}
es6Fuc(4) // 4, default
复制代码
ES6新增了双冒号运算符,用来取代以往的bind,call,和apply。(浏览器暂不支持,Babel已经支持转码)
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
复制代码
答: Symbol是ES6引入的第七种原始数据类型(说法不准确,应该是第七种数据类型,Object不是原始数据类型之一,已更正),所有Symbol()生成的值都是独一无二的,可以从根本上解决对象属性太多导致属性名冲突覆盖的问题。对象中Symbol()属性不能被for…in遍历,但是也不是私有属性。
答: Set是ES6引入的一种类似Array的新的数据结构,Set实例的成员类似于数组item成员,区别是Set实例的成员都是唯一,不重复的。这个特性可以轻松地实现数组去重。
答: Map是ES6引入的一种类似Object的新的数据结构,Map可以理解为是Object的超集,打破了以传统键值对形式定义对象,对象的key不再局限于字符串,也可以是Object。可以更加全面的描述对象的属性。
答: Proxy是ES6新增的一个构造函数,可以理解为JS语言的一个代理,用来改变JS默认的一些语言行为,包括拦截默认的get/set等底层方法,使得JS的使用自由度更高,可以最大限度的满足开发者的需求。比如通过拦截对象的get/set方法,可以轻松地定制自己想要的key或者value。下面的例子可以看到,随便定义一个myOwnObj的key,都可以变成自己想要的函数。
function createMyOwnObj() {
//想把所有的key都变成函数,或者Promise,或者anything
return new Proxy({}, {
get(target, propKey, receiver) {
return new Promise((resolve, reject) => {
setTimeout(() => {
let randomBoolean = Math.random() > 0.5;
let Message;
if (randomBoolean) {
Message = `你的${propKey}运气不错,成功了`;
resolve(Message);
} else {
Message = `你的${propKey}运气不行,失败了`;
reject(Message);
}
}, 1000);
});
}
});
}
let myOwnObj = createMyOwnObj();
myOwnObj.hahaha.then(result => {
console.log(result) //你的hahaha运气不错,成功了
}).catch(error => {
console.log(error) //你的hahaha运气不行,失败了
})
myOwnObj.wuwuwu.then(result => {
console.log(result) //你的wuwuwu运气不错,成功了
}).catch(error => {
console.log(error) //你的wuwuwu运气不行,失败了
})
复制代码
答: Reflect是ES6引入的一个新的对象,他的主要作用有两点,一是将原生的一些零散分布在Object、Function或者全局函数里的方法(如apply、delete、get、set等等),统一整合到Reflect上,这样可以更加方便更加统一的管理一些原生API。其次就是因为Proxy可以改写默认的原生API,如果一旦原生API别改写可能就找不到了,所以Reflect也可以起到备份原生API的作用,使得即使原生API被改写了之后,也可以在被改写之后的API用上默认的API。
答: Promise是ES6引入的一个新的对象,他的主要作用是用来解决JS异步机制里,回调机制产生的“回调地狱”。它并不是什么突破性的API,只是封装了异步回调形式,使得异步回调可以写的更加优雅,可读性更高,而且可以链式调用。
答: Iterator是ES6中一个很重要概念,它并不是对象,也不是任何一种数据类型。因为ES6新增了Set、Map类型,他们和Array、Object类型很像,Array、Object都是可以遍历的,但是Set、Map都不能用for循环遍历,解决这个问题有两种方案,一种是为Set、Map单独新增一个用来遍历的API,另一种是为Set、Map、Array、Object新增一个统一的遍历API,显然,第二种更好,ES6也就顺其自然的需要一种设计标准,来统一所有可遍历类型的遍历方式。Iterator正是这样一种标准。或者说是一种规范理念。
就好像JavaScript是ECMAScript标准的一种具体实现一样,Iterator标准的具体实现是Iterator遍历器。Iterator标准规定,所有部署了key值为[Symbol.iterator],且[Symbol.iterator]的value是标准的Iterator接口函数(标准的Iterator接口函数: 该函数必须返回一个对象,且对象中包含next方法,且执行next()能返回包含value/done属性的Iterator对象)的对象,都称之为可遍历对象,next()后返回的Iterator对象也就是Iterator遍历器。
//obj就是可遍历的,因为它遵循了Iterator标准,且包含[Symbol.iterator]方法,方法函数也符合标准的Iterator接口规范。
//obj.[Symbol.iterator]() 就是Iterator遍历器
let obj = {
data: [ 'hello', 'world' ],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
};
复制代码
ES6给Set、Map、Array、String都加上了[Symbol.iterator]方法,且[Symbol.iterator]方法函数也符合标准的Iterator接口规范,所以Set、Map、Array、String默认都是可以遍历的。
//Array
let array = ['red', 'green', 'blue'];
array[Symbol.iterator]() //Iterator遍历器
array[Symbol.iterator]().next() //{value: "red", done: false}
//String
let string = '1122334455';
string[Symbol.iterator]() //Iterator遍历器
string[Symbol.iterator]().next() //{value: "1", done: false}
//set
let set = new Set(['red', 'green', 'blue']);
set[Symbol.iterator]() //Iterator遍历器
set[Symbol.iterator]().next() //{value: "red", done: false}
//Map
let map = new Map();
let obj= {map: 'map'};
map.set(obj, 'mapValue');
map[Symbol.iterator]().next() {value: Array(2), done: false}
复制代码
答: 如果看到问题十六,那么就很好回答。问题十六提到了ES6统一了遍历标准,制定了可遍历对象,那么用什么方法去遍历呢?答案就是用for…of。ES6规定,有所部署了载了Iterator接口的对象(可遍历对象)都可以通过for…of去遍历,而for…in仅仅可以遍历对象。
这也就意味着,数组也可以用for…of遍历,这极大地方便了数组的取值,且避免了很多程序用for…in去遍历数组的恶习。
上面提到的扩展运算符本质上也就是for…of循环的一种实现。
答: 如果说JavaScript是ECMAScript标准的一种具体实现、Iterator遍历器是Iterator的具体实现,那么Generator函数可以说是Iterator接口的具体实现方式。
执行Generator函数会返回一个遍历器对象,每一次Generator函数里面的yield都相当一次遍历器对象的next()方法,并且可以通过next(value)方法传入自定义的value,来改变Generator函数的行为。
Generator函数可以通过配合Thunk 函数更轻松更优雅的实现异步编程和控制流管理。
答: async函数可以理解为内置自动执行器的Generator函数语法糖,它配合ES6的Promise近乎完美的实现了异步编程解决方案。
答: ES6 的class可以看作只是一个ES5生成实例对象的构造函数的语法糖。它参考了java语言,定义了一个类的概念,让对象原型写法更加清晰,对象实例化更像是一种面向对象编程。Class类可以通过extends实现继承。它和ES5构造函数的不同点:
a. 类的内部定义的所有方法,都是不可枚举的。
///ES5
function ES5Fun (x, y) {
this.x = x;
this.y = y;
}
ES5Fun.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
}
var p = new ES5Fun(1, 3);
p.toString();
Object.keys(ES5Fun.prototype); //['toString']
//ES6
class ES6Fun {
constructor (x, y) {
this.x = x;
this.y = y;
}
toString () {
return '(' + this.x + ', ' + this.y + ')';
}
}
Object.keys(ES6Fun.prototype); //[]
复制代码
b.ES6的class类必须用new命令操作,而ES5的构造函数不用new也可以执行。
c.ES6的class类不存在变量提升,必须先定义class之后才能实例化,不像ES5中可以将构造函数写在实例化之后。
d.ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
答: module、export、import是ES6用来统一前端模块化方案的设计思路和实现方案。export、import的出现统一了前端模块化的实现方案,整合规范了浏览器/服务端的模块化方法,用来取代传统的AMD/CMD、requireJS、seaJS、commondJS等等一系列前端模块不同的实现方案,使前端模块化更加统一规范,JS也能更加能实现大型的应用程序开发。
import引入的模块是静态加载(编译阶段加载)而不是动态加载(运行时加载)。
import引入export导出的接口值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
答:
1、常用箭头函数来取代var self = this;
的做法。
2、常用let取代var命令。
3、常用数组/对象的结构赋值来命名变量,结构更清晰,语义更明确,可读性更好。
4、在长字符串多变量组合场合,用模板字符串来取代字符串累加,能取得更好地效果和阅读体验。
5、用Class类取代传统的构造函数,来生成实例化对象。
6、在大型应用开发中,要保持module模块化开发思维,分清模块之间的关系,常用import、export方法。
MVVM 是 Model-View-ViewModel 的缩写。
Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
beforeCreate(创建前) 在数据观测和初始化事件还未开始
created(创建后) 完成数据观测,属性和方法的运算,初始化事件,
e
l
属性还没有显示出来
∗
∗
b
e
f
o
r
e
M
o
u
n
t
∗
∗
(载入前)在挂载开始之前被调用,相关的
r
e
n
d
e
r
函数首次被调用。实例已完成以下的配置:编译模板,把
d
a
t
a
里面的数据和模板生成
h
t
m
l
。注意此时还没有挂载
h
t
m
l
到页面上。
∗
∗
m
o
u
n
t
e
d
∗
∗
(载入后)在
e
l
被新创建的
v
m
.
el属性还没有显示出来 **beforeMount**(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。 **mounted**(载入后) 在el 被新创建的 vm.
el属性还没有显示出来∗∗beforeMount∗∗(载入前)在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。∗∗mounted∗∗(载入后)在el被新创建的vm.el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
1.什么是vue生命周期?
答: Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
2.vue生命周期的作用是什么?
答:它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
3.vue生命周期总共有几个阶段?
答:它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后。
4.第一次页面加载会触发哪几个钩子?
答:会触发 下面这几个beforeCreate, created, beforeMount, mounted 。
5.DOM 渲染在 哪个周期中就已经完成?
答:DOM 渲染在 mounted 中就已经完成了。
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过**Object.defineProperty()**来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
js实现简单的双向绑定
<body>
<div id="app">
<input type="text" id="txt">
<p id="show"></p>
</div>
</body>
<script type="text/javascript">
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
document.addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
</script>
1.父组件与子组件传值
父组件传给子组件:子组件通过props方法接受数据;
子组件传给父组件:$emit方法传递参数
2.非父子组件间的数据传递,兄弟组件传值
eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适。(虽然也有不少人推荐直接用VUEX,具体来说看需求咯。技术只是手段,目的达到才是王道。)
**hash模式:**在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
**history模式:**history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
(版本在不断更新,以下的区别有可能不是很正确。我工作中只用到vue,对angular和react不怎么熟)
1.与AngularJS的区别
相同点:
都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器。
不同点:
AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观;在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。
2.与React的区别
相同点:
React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用;中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数,可以让开发者定制化地去处理需求;都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载;在组件开发中都支持mixins的特性。
不同点:
React采用的Virtual DOM会对渲染出来的结果做脏检查;Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM。
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。
beforeEach主要有3个参数to,from,next:
to:route即将进入的目标路由对象,
from:route当前导航正要离开的路由
next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。
只用来读取的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。
在main.js引入store,注入。新建了一个目录store,…… export 。
场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-biCZCx5s-1611887935896)(C:\Users\zjx\Desktop\桌面文件\web面试\Vuepng.png)]
state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters
类似vue的计算属性,主要用来过滤一些数据。
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。
const store = new Vuex.Store({ //store实例
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
})
1.创建局部指令
var app = new Vue({
el: '#app',
data: {
},
// 创建指令(可以多个)
directives: {
// 指令名称
dir1: {
inserted(el) {
// 指令中第一个参数是当前使用指令的DOM
console.log(el);
console.log(arguments);
// 对DOM进行操作
el.style.width = '200px';
el.style.height = '200px';
el.style.background = '#000';
}
}
}
})
2.全局指令
Vue.directive('dir2', {
inserted(el) {
console.log(el);
}
})
3.指令的使用
<div id="app">
<div v-dir1></div>
<div v-dir2></div>
</div>
html代码:
<div id="app">
<input type="text" v-model="msg" />
{{msg| capitalize }}
</div>
JS代码:
var vm=new Vue({
el:"#app",
data:{
msg:''
},
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})
全局定义过滤器
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
过滤器接收表达式的值 (msg) 作为第一个参数。capitalize 过滤器将会收到 msg的值作为第一个参数。
keep-alive是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
在vue 2.1.0 版本之后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。
使用方法
<keep-alive include='include_components' exclude='exclude_components'>
<component>
<!-- 该组件是否缓存取决于include和exclude属性 -->
</component>
</keep-alive>
参数解释
include - 字符串或正则表达式,只有名称匹配的组件会被缓存
exclude - 字符串或正则表达式,任何名称匹配的组件都不会被缓存
include 和 exclude 的属性允许组件有条件地缓存。二者都可以用“,”分隔字符串、正则表达式、数组。当使用正则或者是数组时,要记得使用v-bind 。
使用示例
<!-- 逗号分隔字符串,只有组件a与b被缓存。 -->
<keep-alive include="a,b">
<component></component>
</keep-alive>
<!-- 正则表达式 (需要使用 v-bind,符合匹配规则的都会被缓存) -->
<keep-alive :include="/a|b/">
<component></component>
</keep-alive>
<!-- Array (需要使用 v-bind,被包含的都会被缓存) -->
<keep-alive :include="['a', 'b']">
<component></component>
</keep-alive>
1.css只在当前组件起作用
答:在style标签中写入scoped即可 例如:
2.v-if 和 v-show 区别
答:v-if按照条件是否渲染,v-show是display的block或none;
3.
r
o
u
t
e
和
route和
route和router的区别
答:$route
是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。而$router
是“路由实例”对象包括了路由的跳转方法,钩子函数等。
4.vue.js的两个核心是什么?
答:数据驱动、组件系统
5.vue几种常用的指令
答:v-for 、 v-if 、v-bind、v-on、v-show、v-else
6.vue常用的修饰符?
答:.prevent: 提交事件不再重载页面;.stop: 阻止单击事件冒泡;.self: 当事件发生在该元素本身而不是子元素的时候会触发;.capture: 事件侦听,事件发生的时候会调用
7.v-on 可以绑定多个方法吗?
答:可以
8.vue中 key 值的作用?
答:当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key的作用主要是为了高效的更新虚拟DOM。
9.什么是vue的计算属性?
答:在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。好处:①使得数据处理结构清晰;②依赖于数据,数据更新,处理结果自动更新;③计算属性内部this指向vm实例;④在template调用时,直接写计算属性名即可;⑤常用的是getter方法,获取数据,也可以使用set方法改变数据;⑥相较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算。
10.vue等单页面应用及其优缺点
答:优点:Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统。MVVM、数据驱动、组件化、轻量、简洁、高效、快速、模块友好。
缺点:不支持低版本的浏览器,最低只支持到IE9;不利于SEO的优化(如果要支持SEO,建议通过服务端来进行渲染组件);第一次加载首页耗时相对长一些;不可以使用浏览器的导航按钮需要自行实现前进、后退。
11.怎么定义 vue-router 的动态路由? 怎么获取传过来的值
答:在 router 目录下的 index.js 文件中,对 path 属性加上 /:id,使用 router 对象的 params.id 获取。
思路:回文是指把相同的词汇或句子,在下文中调换位置或颠倒过来,产生首尾回环的情景,叫做回文,也叫回环。
将一个字符串首尾倒序排列,如果与原字符串相等,则这个字符串回文。
<script type="text/javascript">
var str1 = 'abcdefgh';
var str2 = 'abcdcba';
function plalindrome(str){
return str == str.split('').reverse().join('');
}
console.log(plalindrome(str1));//false
console.log(plalindrome(str2));//true
</script>
思路:利用indexOf()a方法,在遍历原数组,若里面的元素第一次出现,则放在数组arr1中,遍历完之后,arr1中存放的是无重复的新数组
<script type="text/javascript">
var arr = [2,4,2,2,5,6,7,8,9,9,9];
function unique(arr){
var arr1 = [];
for (var i = 0;i < arr.length;i ++){
if(arr1.indexOf(arr[i]) == -1){
arr1.push(arr[i]);
}
}
return arr1;
}
console.log(unique(arr));//[2, 4, 5, 6, 7, 8, 9]
</script>
思路:在另外一个数组存放原数组每个元素出现的位置次数,且次数跟存放不重复数组的下标对应,然后取出最多的次数,对应的下标就是不重复数组里面那个出现次数最多的元素的下标
<script type="text/javascript">
var str1 = "jhadfgskjfajhdewqe";
var arr1 = str1.split('');
console.log(arr1);
function MostUnit(){
var arrA = [];
var arrB = [];
for(var i = 0 ;i <arr1.length; i ++){
if(arrA.indexOf(arr1[i])==-1){
arrA.push(arr1[i]);
arrB.push(1);
}else {
arrB[arrA.indexOf(arr1[i])] ++;
}
}
console.log(arrB)
console.log(arrA[arrB.indexOf(Math.max.apply(Math,arrB))]);
}
MostUnit();//j
</script>
<script type="text/javascript">
var arr1 = [2,3,45,64,321,3,21,321,31,999];
function bubbleSort(arr) {
for(var i = 0 ;i < arr1.length-1 ;i ++){
for(var j = 0; j < arr1.length - i - 1 ;j ++){
if(arr[j]>arr[j+1]) {
let tem = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tem;
}
}
}
return arr;
}
console.log(bubbleSort(arr1));//[2, 3, 3, 21, 31, 45, 64, 321, 321, 999]
</script>
思路:算法参考某个元素值,将小于它的值,放到左数组中,大于它的值的元素就放到右数组中,然后递归进行上一次左右数组的操作,返回合并的数组就是已经排好顺序的数组了。
<script type="text/javascript">
var arr1 = [1,4,765,86,53,87,53,32,6,64,2,3,767,34,1,4,35,6];
function quickSort(arr){
if(arr.length <= 1){
return arr;
}
var leftArr = [];
var rightArr = [];
var q = arr[0];
for(var i = 1;i < arr.length; i++) {
if(arr[i]>q) {
rightArr.push(arr[i]);
}else{
leftArr.push(arr[i]);
}
}
return [].concat(quickSort(leftArr),[q],quickSort(rightArr));
}
console.log(quickSort(arr1));//[1,4,765,86,53,87,53,32,6,64,2,3,767,34,1,4,35,6]
</script>
思路:利用两个元素的差值进行计算
<script type="text/javascript">
var a = 10;
var b = 12;
function swap (a,b) {
b = b - a;
a = a + b;
b = a - b;
return [a,b]
}
console.log(swap(a,b));
</script>
<script type="text/javascript">
var arr1 = [2,44,3,-12,43,5,8,67,54,32,-211];
var max = Math.max.apply(Math,arr1);
var min = Math.min.apply(Math,arr1);
console.log(max-min);//278
</script>
思路:charAt()方法,获取元素下标
<script type="text/javascript">
function randomString(n){
var str1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz9876543210"
var str2 = "";
for (var i = 0; i < n ; i ++){
str2 += str1.charAt(Math.floor(Math.random()*str1.length));
}
return str2;
}
console.log(randomString(5));
</script>
<div id="text">
<div class="cs"></div>
<div class="as"></div>
<p class="cs"></p>
</div>
<script type="text/javascript">
function getClass(node,classname) {
if(node.getElementsByClassName) {
return node.getElementsByClassName(classname);
//如果存在该标签 就返回
} else {
var elems = node.getElementsByTagName(node),
defualt = [];
for (var i = 0; i < elems.length; i++) {
//遍历所有标签
if(elems[i].className.indexOf(classname) != -1) {
//查找相应类名的标签
defualt[defualt.length] = elems[i];
}
}
return defualt;
}
}
var text = document.getElementById('text'),
cs = getClass(text,'cs');
console.log(cs);//[div.cs, p.cs]
</script>
一说到二叉树我们肯定会问,什么是二叉树,二叉树是个啥子东东,拿来有啥子用嘛,我们为啥子要学习它嘛? 如果当初你在学习二叉树的时候你没有问过自己这些问题,那么你对它的了解也仅仅也只是了解。那我们现在来说说什么是二叉树,二叉树就是一种数据结构, 它的组织关系就像是自然界中的树一样。官方语言的定义是:是一个有限元素的集合,该集合或者为空、或者由一个称为根的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成。至于为啥子要学习它,妈妈总是说,孩子,等你长大了就明白了。
性质1:二叉树第i层上的节点数目最多为2i-1(i≥1);
性质2:深度为k的二叉树至多有2k-1个结点(k≥1)。
性质3: 在任意-棵二叉树中,若叶子结点(即度为0的结点)的个数为n0,度为1的结点数为n1,度为2的结点数为n2,则no=n2+1。
二叉树的存储方式有两种,一种顺序存储,比如:
var binaryTree = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘h’, ‘i’]; 这就是一颗二叉树,假设binaryTree[i]是二叉树的一个节点,那么它的左孩子节点 leftChild = binaryTree[i2+1]那 么相应的右孩子节点 rightChild = binaryTree[i2+2]; 一般情况下顺序存储的这种结构用的较少,另外一种存储方式就是链式存储,下面我会用代码来详细描述二叉树式 结构的构建与存储方式,构建二叉树也有两种方式一种是递归方式构建,这种很简单,另一种是非递归方法构建,这种呢相对于前一种复杂一点点,不过也不用担心,我在 代码中加上详细的注释,一步一步的走下去。我们现在就以26个英文字母来构建二叉树
var charecters = [‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘O’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’, ‘Y’, ‘Z’];
在构建二叉树之前我们会用到一个节点对象,节点对象如下:(注意:关于javascript的面向对象,原型,语法特点我会放在javascript语言知识点这个系列)
/*
*二叉树的节点对象
*/
function Node() {
this.text = ''; //节点的文本
this.leftChild = null; //节点的左孩子引用
this.rightChild = null; //节点右孩子引用
}
在构建好二叉树节点之后我们紧接着用递归来构建二叉树
var charecters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
function buildTree(node, i) {
var leftIndex = 2*i+1, //左孩子节点的索引
rightIndex = 2*i+2; //右孩子节点的索引
if(leftIndex < charecters.length) { //判断索引的长度是否超过了charecters数组的大小
var childNode = new Node(); //创建一个新的节点对象
childNode.text = charecters[leftIndex]; //给节点赋值
node.leftChild = childNode; //给当前节点node加入左孩子节点
buildTree(childNode, leftIndex); //递归创建左孩子
}
if(rightIndex < charecters.length) { //下面注释参照上面的构建左孩子的节点
var childNode = new Node();
childNode.text = charecters[rightIndex];
node.rightChild = childNode;
buildTree(childNode, rightIndex);
}
}
//下面构造二叉树
var node = new Node();
node.text = charecters[0];
buildTree(node, 0); //索引i是从0开始构建
下面是以非递归方式构建二叉树:
var root;
function createBinaryTree() {
var len = charecters.length, //数组的长度
index = 0, //索引从0开始
nodes = new Array(); //创建一个临时数组,用于存放二叉树节点
//循环创建二叉树节点存放到数组中
for (var i = 0 ; i < charecters.length ; i++) {
var node = new Node();
node.text = charecters[i];
nodes.push(node);
}
//循环建立二叉树子节点的引用
while(index < len) {
var leftIndex = 2*index+1, //当前节点左孩子索引
rightIndex = 2*index+2; //当前节点右孩子索引
//给当前节点添加左孩子
nodes[index].leftChild = nodes[leftIndex];
//给当前节点添加右孩子
nodes[index].rightChild = nodes[rightIndex];
index++;
}
root = nodes[0];
}
好了,现在我们已经成功构建了二叉树的链式结构,在构建了二叉树的链式结构后我们进入二叉树的最基本的遍历了,遍历有三种最基本的遍历,我不说想必大家都知道,先序遍历,中序遍历和后续遍历。虽然这三种遍历递归方式都比较简单,但非递归方式就不是那么容易了,当时我在实现的时候都卡了半天,真的是说起来容易做起来难啊,在实现遍历前我们首先要来实现的是栈,因为在非递归遍历的时候会用到栈,那到底什么是栈呢,这里我就简单介绍下吧,有兴趣的朋友可以去维基百科有权威的定义,栈和队列也是一种数据结构,栈存放数据的时候是先进先出,而队列是先进后出。
下面用javascript来实现栈的对象
function Stack() {
var stack = new Array(); //存放栈的数组
//压栈
this.push = function(o) {
stack.push(o);
};
//出栈
this.pop = function() {
var o = stack[stack.length-1];
stack.splice(stack.length-1, 1);
return o;
};
//检查栈是否为空
this.isEmpty = function() {
if(stack.length <= 0) {
return true;
}
else {
return false;
}
};
}
//使用方式如下
var stack = new Stack();
stack.push(1); //现在栈中有一个元素
stack.isEmpty(); //false , 栈不为空
alert(stack.pop()); //出栈, 打印1
stack.isEmpty(); //true, 此时栈为空,因为在调用了stack.pop()之后元素出栈了,所以为空
在实现了栈对象以后我们首先来进行先序遍历的递归方式
function firstIteration(node) {
if(node.leftChild) { //判断当前节点是否有左孩子
firstIteration(node.leftChild); //递归左孩子
}
if(node.rightChild) { //判断当前节点是否有右孩子
firstIteration(node.rightChild); //递归右孩子
}
}
//递归遍历二叉树
firstIteration(root);
上面的代码大家可以在firstIteration()方法中加个alert()函数来验证是否正确。那么下面就要说说先序遍历的非递归方式,遍历思想是这样的:先访问根节点在访问左节 点, 最后访问右节点。从根节点一直往下访问找左孩子节点,直到最后一个左孩子节点(将这条路径保存到栈中),然后再访问最后一个左孩子的兄弟节点(右孩子节点),之后回溯到上一层(将栈中的元素取出 就是出栈),又开始从该节点(回溯到上一层的节点)一直往下访问找左孩子节点… 直到栈中的元素为空,循环结束。
function notFirstIteration(node) {
var stack = new Stack(), //开辟一个新的栈对象
resultText = ''; //存放非递归遍历之后的字母顺序
stack.push(root); //这个root在上面非递归方式构建二叉树的时候已经构建好的
var node = root;
resultText += node.text;
while(!stack.isEmpty()) {
while(node.leftChild) { //判断当前节点是否有左孩子节点
node = node.leftChild; //取当前节点的左孩子节点
resultText += node.text; //访问当前节点
stack.push(node); //将当前节点压入栈中
}
stack.pop(); //出栈
node = stack.pop().rightChild; //访问当前节点的兄弟节点(右孩子节点)
if(node) { //当前节点的兄弟节点不为空
resultText += node.text; //访问当前节点
stack.push(node); //将当前节点压入栈中
}
else { //当前节点的兄弟节点为空
node = stack.pop(); //在回溯到上一层
}
}
}
//非递归先序遍历
notFirstIteration(root);
只要把思路理清楚了现实起来其实还是挺容易的,只要我们熟悉了一种二叉树的非递归遍历方式,其他几种非递归方式就容易多了,照着葫芦画瓢,下面是中序遍历的递归 方式,中序遍历的思想是:先访问左孩子节点,在访问根节点,最后访问右节点
var strText = "";
function secondIteration(node) {
//访问左节点
if(node.leftChild) {
if(node.leftChild.leftChild) {
secondIteration(node.leftChild);
}
else {
strText += node.leftChild.text;
}
}
//访问根节点
strText += node.text;
//访问右节点
if(node.rightChild) {
if(node.rightChild.leftChild) {
secondIteration(node.rightChild);
}
else {
strText += node.rightChild.text;
}
}
}
secondIteration(root);
alert(strText);
思想是:1. 从根节点一直往下找左孩子节点,直到找到最后一个左孩子节点(用栈将此路径保存,但不访问)2.访问最后一个左孩子节点,然后再 访问根节点(要先弹出栈,就是在栈中取上一层节点)3.在访问当前节点(最后一个左孩子节点)的兄弟节点(右孩子节点),这里要注意如果兄弟节点是一个叶节点就直 接访问,否则是兄弟节点是一颗子树的话不能马上访问,要先来重复 1, 2,3步骤, 直到栈为空,循环结束
function notSecondIteration() {
var resultText = '',
stack = new Stack(),
node = root;
stack.push(node);
while(!stack.isEmpty()) {
//从根节点一直往下找左孩子节点直到最后一个左孩子节点,然后保存在栈中
while(node.leftChild) {
node = node.leftChild;
stack.push(node);
}
//弹出栈
var tempNode = stack.pop();
//访问临时节点
resultText += tempNode.text;
if(tempNode.rightChild) {
node = tempNode.rightChild;
stack.push(node);
}
}
alert(resultText);
}
最后就还剩下一种遍历方式,二叉树的后续遍历,后续遍历的思想是:先访问左孩子节点,然后在访问右孩子节点,最后访问根节点
var strText = '';
function lastIteration(node) {
//首先访问左孩子节点
if(node.leftChild) {
if(node.leftChild.leftChild) {
lastIteration(node.leftChild);
}
else {
strText += node.leftChild.text;
}
}
//然后再访问右孩子节点
if(node.rightChild) {
if(node.rightChild.rightChild) {
lastIteration(node.rightChild);
}
else {
strText += node.rightChild.text;
}
}
//最后访问根节点
strText += node.text;
}
//中序递归遍历
lastIteration(root);
alert(strText);
后续非递归遍历的思想是:1.从根节点一直往下找左孩子节点,直到最后一个左孩子节点(将路径保存到栈中,但不访问)2.弹出栈访问最后一个左孩子节点 3.进入最后一 个左孩子节点的兄弟节点,如果兄弟节点是叶节点就访问它,否则将该节点重复 1, 2步骤, 直到栈中的元素为空,循环结束。3.访问根节点
function notLastIteration() {
var strText = '',
stack = new Stack();
nodo = root;
stack.push(node);
while(!stack.isEmpty()) {
while(node.leftChild) {
node = node.leftChild;
stack.push(node);
}
//弹出栈
var tempNode = stack.pop();
//访问左孩子节点
strText += tempNode.text;
//访问右孩子节点
if(tempNode.rightChild) {
if(tempNode.rightChild.leftChild || tempNode.rightChild.rightChild) { //判断最后一个左孩子节点的兄弟节点是否为页节点
stack.push(tempNode.rightChild);
}
else {
strText += tempNode.rightChild.text;
}
}
}
alert(strText);
}
<script>
let qianxu = [ 1,2,4,7,3,5,6,8 ];
let zhongxu = [ 4,7,2,1,5,3,8,6 ];
function TreeNode( val ){
this.val = val;
this.left = null;
this.right = null;
}
function rebuildTree(qianxu,zhongxu){
if (qianxu[0]){
// 1. 根据找到的根节点( 前序序列的第一个元素一定是根节点 )
let rootVal = qianxu[0];
// 2. 找到根节点和中序序列,找到树的左子树和右子树
// 根节点在中序序列中的位置
let index = zhongxu.indexOf( rootVal );
// 前序序列:左子树 qianxu(1,index),右子树qianxu(index+1,最后)
// 中序序列:左子树 zhongxu(0,index-1),右子树 zhongxu(index+1,最后)
let leftTree = rebuildTree(qianxu.slice(1,index+1),zhongxu.slice(0,index));
let rightTree = rebuildTree(qianxu.slice(index+1),zhongxu.slice(index+1));
let root = new TreeNode(rootVal);
root.right = rightTree;
root.left = leftTree;
return root;
}
}
console.log(rebuildTree(qianxu,zhongxu));
</script>
<script>
let stack1 = [];
let stack2 = [];
function push(node){
stack1.push(node);
}
function pop (){
while(stack1.length){
stack2.push(stack1.pop());
}
let popVal = stack2.pop();
while(stack2.length){
stack1.push(stack2.pop());
}
return popVal;
}
/**
* 1,2,3,4,5 入队
* 出队操作 1
* 入队 6
* 出队操作2
* 出队操作3
*/
push(1);
push(2);
push(3);
push(4);
push(5);
console.log(pop());
</script>
<script>
function jumpFloor(n){
if(n==1) return 1;
else if(n==2) return 2;
return jumpFloor(n-1)+jumpFloor(n-2);
}
console.log(jumpFloor(3));
</script>
<script>
// 记忆化递归
let cache = [,1,2];
function jumpFloor2(n){
if(cache[n] !==undefined) return cache[n];
return cache[n] = jumpFloor(n-1)+jumpFloor(n-2);
}
console.log(jumpFloor2(3));
</script>
<script>
let cache = [,1,2];
function jumpFloor(n){
if(cache[n] !== undefined) return cache[n];
cache[n] = 1;
for(let i = n-1;i >=1;i--){
cache[n] += jumpFloor(i);
}
return cache[n];
}
console.log(jumpFloor(10));
</script>
<script>
function Node(val){
this.val = val;
this.next = null;
}
function creatList(arr){
let head = new Node(arr[0]);
let tail = head;
for(let i =1;i<=arr.length-1;i++){
tail.next = new Node(arr[i]);
tail = tail.next;
}
return head;
}
let list = creatList([1,2,3,4,5]);
// console.log(list);
function reverseList (head){
let arr = [];
let p = head;
while(p){
arr.push(p.val);
p = p.next;
}
p = head;
while(p){
p.val = arr.pop(p.val);
p = p.next;
}
return head;
}
console.log(reverseList(list));
</script>
需求:
10 创建一个字典树,在字典树中查找是否包含某个单词
11
12 单词序列:
13 and
14 about
15 as
16 boy
17 by
18 because
19 as
20
21 查找:
22 close false
23 an false
24 as true
25 boy true
26
27 字典树是什么
28 字典树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
30 字典树的作用
31 统计,排序和保存大量的字符串
33 字典树的特点
34 1、字典树的节点存储的是单词的字符(字母)
35 2、为了表示一个单词是否出现,我们可以给单词的最后的字符加上标记
36 3、字典树中表示一个单词用的是一条链
37 4、字典树的根节点没有什么意义
39 字典树的操作
40 1、把单词插入到字典树里面去
41 2、在字典树中查找单词
45 1、把单词插入到字典树里面去
46 算法步骤:
47 去跟节点下面去找这个单词的第一个字符是否出现,
48 如果没出现,就创建,然后走这条路,
49 如果出现了,就直接走这条路
50 (在这个过程里面,单词的第一个字符就被消耗掉了)
52 算法:
53 递归
56 2、在字典树中查找单词
57 算法:
58 递归
60 算法步骤:
61 查找单词的第一个字符是否在根节点的子节点中,如果出现了,就接着往下找
62 如果没出现,直接return false
63 在单词找完后,如果标记大于1,表示单词出现过,就return true,
64 否则return false
1. 它是一个Javascript运行环境
2. 依赖于Chrome V8引擎进行代码解释
3. 事件驱动
4. 非阻塞I/O
5. 轻量、可伸缩,适于实时数据交互应用
6. 单进程,单线程
异步 摆脱回调地域
对response 和request进行了封装 content
Express主要基于Connect中间件框架,功能丰富,随取随用,并且框架自身封装了大量便利的功能,比如路由、视图处理等等。而koa主要基于co中间件框架,框架自身并没集成太多功能,大部分功能需要用户自行require中间件去解决,但是由于其基于ES6 generator特性的中间件机制,解决了长期诟病的“callback hell”和麻烦的错误处理的问题,大受开发者欢迎。
事件驱动模型:当服务端收到请求时,就把它关闭 然后处理下一个请求 当第一个请求处理完毕后 就放回处理队列 当达到队列开头 将结果返回给用户 好处:高效 扩展性强 因为服务端一直接受请求 不等待任何读写操作
事件循环:查看队列里面是否有队列里面有待处理的 如果有 交给主线程执行
使用场景:支持string、list、set、zset和hash类型数据。
mysql 关系型数据库 mongodb是非关系数据库(主要)
六:MySQL索引
七:闭包应该注意的地方
八:进程和线程
进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
进程是线程的容器
InnoDB存储引擎:事务型数据库首选,支持事务安全表(ACID),支持行锁定和外键 是mysql 5.5之后的默认引擎
MyISAM 存储引擎:不支持事务和外键,访问速度较快,是mysql5.5 之前的默认引擎
MEMORY: 保存在内存中的数据表 ,每个memory表对应一个磁盘文件。格式是.frm 访问速度很快 缺点是:mysql服务关闭,数据丢失,另外对数据表大小有限制。
indexof es6:include startWith endWith
十二:单点登录
十三:oauth2.0
十四:type of 和instance of 区别
十五:pm2 restart 和reload的区别(配置文件的重载 重启)
十六:MySQL 读写分离
十七:pm2如何查看指定三个项目的日志
十八:深拷贝 浅拷贝
十九:路由机制
二十:MySQL 批量更新
二十一:登录流程
二十二:cookie 和session
二十三:基本数据类型 引用数据类型 区别
1.使用escape() 对传入参数进行编码
2.使用connection.query ()的查询参数占位符
3.使用escapeId()编码SQL查询标识符
4.使用mysql.format()转义参数:
先判断是否存在文件缓存区中,存在直接导入,没有的话,在判断是否是原生模块,
如果是原生模块,再看是否在原生模块缓存区中,如果有直接导入,没有的话加载原生模块,缓存原生模块,在导入
如果不是原生模块,先查找文件模块,根据扩展名载入文件模块,缓存文件模块,在导入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rujkgYx7-1611887935898)(C:\Users\zjx\Desktop\桌面文件\web面试\1.jpg)]
Nodejs是服务器端的一门技术。它是基于Google V8 JavaScript引擎而开发的。用来开发可扩展的服务端程序。
nodejs会让我们的编程工作变得简单,它主要包含如下几点几个好处:
执行快速。
永远不会阻滞。
JavaScript是通用的编程语言。
异步处理机制。
避免并行所带来的问题。
是单线程的,但是有很高的可扩展性,使用JavaScript作为主流编程语言。使用的是异步处理机制和事件驱动。处理高效。
Set immediate就是马上执行的意思。Set time out, 时间参数传为0,也想获得同样的功能。只不过前者要快一些。
npm install npm -g
Nodejs使用的是单线程没错,但是通过异步处理的方式,可以处理大量的数据吞吐量,从而有更好的性能和扩可扩展性。
回调函数是指用一个函数作为参数传入另一个函数,这个函数会被在某个时机调用。
回调地狱是由嵌套的回调函数导致的。这样的机制会导致有些函数无法到达,并且很难维护。
有三种方法, 对每个错误都要处理到, 保证代码的贯通, 程序代码模块化。
Read evaluate print loop, 用于测试,调试和实验用。
有两种,
一种是阻滞型函数。阻滞型函数会等待操作完成以后再进行下一步。
另外一种是非阻滞型函数。这种函数使用回调函数来处理当前函数获取的结果。
通常是错误对象。如果这个参数为空,表示没有错误。
Node package manager, 主要有两个功能。
它是一个网端模块的存储介质。
它的另一个作用是安装程序依赖和版本管理。
Nodejs和ajax也就是asynchronous JavaScript and xml,都是通过JavaScript来表现的,但是他们的目的截然不同。
Ajax是设计用来动态的更新页面的某个区域,从而不需要更新整个页面。
Nodejs是用来开发客户服务器类型应用的。
Chaining是指从一个数据流到另一个数据流的链接,从而实现多个流操作。
流的概念是不间断的,它可以不间断的从某个地方读取数据,或者向某个地方写入数据。
有4种类型的流数据。可读,可写。既可读,又可写,转化。
退出代码是指中断nodejs运行时返回的代码。
有这么几种unused, uncaught fatal exception, fatal error, non function internal exception handler, internal exception handler run time failure,internal JavaScript evaluation failure.
有三个global的关键字。
Global代表的是最上层的命名空间,用来管理所有其他的全局对象。
Process 是一个全局对象,可以把异步函数转化成异步回调, 它可以在任何地方被访问,它主要是用来返回系统的应用信息和环境信息.
Buffer, 是用来处理二进制数据的类.
Angular js是网络应用开发框架,而nodejs是一个实时系统。
统一的风格可以让所有的组成员按照一种规矩来写代码。工具有Standard和eslint.
在应用和node js之间使用domain来处理这样的异常。
Cluster模块是用来支持这方面的。它可以允许多个nodejs工作进程运行在相同的端口上。
首先有一个配置文件,然后通过环境变量参数来获取对应的配置内容。
Set time out, clear time out.
Set interval, clear interval.
Set immediate, clear immediate.
Process.nextTick.
Reactor pattern主要是非阻滞的i/o操作。提供一个回调函数来关联io操作。io请求完成以后会不会提交给demultiplexer, 这是一个通知接口用来处理并发性的非阻滞的io操作,这个功能是通过查询一个event loop来实现的.
也就是long term support版本。至少会被支持18个月。使用的是偶数来标识。这种版本有稳定性和安全性的保证。
分开以后方便维护以及测试,在测试某个模块的时候,尤其是APP模块的时候,你不需要去对网络方面的连接配置做工作。
Next tick会等待当前的event执行完成或者下一轮儿事件循环到达再执行。
Set immediate, 会在下一轮的事件循环中,执行回调并且返回当前的循环来做读写操作.
参考答案:三者都可以把一个函数应用到其他对象上,注意不是自身对象.apply,call是直接执行函数调用,bind是绑定,执行需要再次调用.apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表,
代码演示
function Person() {
}
Person.prototype.sayName() { alert(this.name); }
var obj = {name: 'michaelqin'}; // 注意这是一个普通对象,它不是Person的实例
1) apply
Person.prototype.sayName.apply(obj, [param1, param2, param3]);
2) call
Person.prototype.sayName.call(obj, param1, param2, param3);
3) bind
var sn = Person.prototype.sayName.bind(obj);
sn([param1, param2, param3]); // bind需要先绑定,再执行
sn(param1, param2, param3); // bind需要先绑定,再执行
参考答案: EventEmitter, Stream, FS, Net和全局对象
参考答案: process, console, Buffer
参考答案: process.stdin, process.stdout, process.stderr, process.on, process.env, process.argv, process.arch, process.platform, process.exit
参考答案: console.log/console.info, console.error/console.warning, console.time/console.timeEnd, console.trace, console.table
参考答案: setTimeout/clearTimeout, setInterval/clearInterval, setImmediate/clearImmediate, process.nextTick
总体上执行顺序是:process.nextTick >> setImmidate >> setTimeout/SetInterval 看官网吧:[https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/)
参考答案: Buffer是用来处理二进制数据的,比如图片,mp3,数据库文件等.Buffer支持各种编码解码,二进制字符串互转.
参考答案: EventEmitter是node中一个实现观察者模式的类,主要功能是监听和发射消息,用于处理多模块交互问题.
参考答案: 主要分三步:定义一个子类,调用构造函数,继承EventEmitter
代码演示
var util = require('util');
var EventEmitter = require('events').EventEmitter;
function MyEmitter() {
EventEmitter.call(this);
} // 构造函数
util.inherits(MyEmitter, EventEmitter); // 继承
var em = new MyEmitter();
em.on('hello', function(data) {
console.log('收到事件hello的数据:', data);
}); // 接收事件,并打印到控制台
em.emit('hello', 'EventEmitter传递消息真方便!');
参考答案:
参考答案: 监听error事件即可.如果有多个EventEmitter,也可以用domain来统一处理错误事件.
代码演示
var domain = require('domain');
var myDomain = domain.create();
myDomain.on('error', function(err){
console.log('domain接收到的错误事件:', err);
}); // 接收事件并打印
myDomain.run(function(){
var emitter1 = new MyEmitter();
emitter1.emit('error', '错误事件来自emitter1');
emitter2 = new MyEmitter();
emitter2.emit('error', '错误事件来自emitter2');
});
参考答案: newListener可以用来做事件机制的反射,特殊应用,事件管理等.当任何on事件添加到EventEmitter时,就会触发newListener事件,基于这种模式,我们可以做很多自定义处理.
代码演示
var emitter3 = new MyEmitter();
emitter3.on('newListener', function(name, listener) {
console.log("新事件的名字:", name);
console.log("新事件的代码:", listener);
setTimeout(function(){ console.log("我是自定义延时处理机制"); }, 1000);
});
emitter3.on('hello', function(){
console.log('hello node');
});
参考答案: stream是基于事件EventEmitter的数据管理模式.由各种不同的抽象接口组成,主要包括可写,可读,可读写,可转换等几种类型.
参考答案: 非阻塞式数据处理提升效率,片断处理节省内存,管道处理方便可扩展等.
参考答案: 文件,网络,数据转换,音频视频等.
参考答案: 监听error事件,方法同EventEmitter.
参考答案: Readable为可被读流,在作为输入数据源时使用;Writable为可被写流,在作为输出源时使用;Duplex为可读写流,它作为输出源接受被写入,同时又作为输入源被后面的流读出.Transform机制和Duplex一样,都是双向流,区别时Transfrom只需要实现一个函数_transfrom(chunk, encoding, callback);而Duplex需要分别实现_read(size)函数和_write(chunk, encoding, callback)函数.
参考答案: 三步走:1)构造函数call Writable 2) 继承Writable 3) 实现_write(chunk, encoding, callback)函数
代码演示
var Writable = require('stream').Writable;
var util = require('util');
function MyWritable(options) {
Writable.call(this, options);
} // 构造函数
util.inherits(MyWritable, Writable); // 继承自Writable
MyWritable.prototype._write = function(chunk, encoding, callback) {
console.log("被写入的数据是:", chunk.toString()); // 此处可对写入的数据进行处理
callback();
};
process.stdin.pipe(new MyWritable()); // stdin作为输入源,MyWritable作为输出源
参考答案: fs模块主要由下面几部分组成: 1) POSIX文件Wrapper,对应于操作系统的原生文件操作 2) 文件流 fs.createReadStream和fs.createWriteStream 3) 同步文件读写,fs.readFileSync和fs.writeFileSync 4) 异步文件读写, fs.readFile和fs.writeFile
参考答案: 总体来说有四种: 1) POSIX式低层读写 2) 流式读写 3) 同步文件读写 4) 异步文件读写
参考答案: 主要有两种方式,第一种是利用node内置的require(‘data.json’)机制,直接得到js对象; 第二种是读入文件入内容,然后用JSON.parse(content)转换成js对象.二者的区别是require机制情况下,如果多个模块都加载了同一个json文件,那么其中一个改变了js对象,其它跟着改变,这是由node模块的缓存机制造成的,只有一个js模块对象; 第二种方式则可以随意改变加载后的js变量,而且各模块互不影响,因为他们都是独立的,是多个js对象.
参考答案: 二者主要用来监听文件变动.fs.watch利用操作系统原生机制来监听,可能不适用网络文件系统; fs.watchFile则是定期检查文件状态变更,适用于网络文件系统,但是相比fs.watch有些慢,因为不是实时机制.
参考答案: node全面支持各种网络服务器和客户端,包括tcp, http/https, tcp, udp, dns, tls/ssl等.
参考答案: 主要实现以下几个步骤即可: 1) openssl生成公钥私钥 2) 服务器或客户端使用https替代http 3) 服务器或客户端加载公钥私钥证书
参考答案: 经典又很没毛意义的一个题目.思路是加载http模块,创建服务器,监听端口.
代码演示
var http = require('http'); // 加载http模块
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'}); // 200代表状态成功, 文档类型是给浏览器识别用的
res.write('<meta charset="UTF-8"> <h1>我是标题啊!</h1> <font color="red">这么原生,初级的服务器,下辈子能用着吗?!</font>'); // 返回给客户端的html数据
res.end(); // 结束输出流
}).listen(3000); // 绑定3ooo, 查看效果请访问 http://localhost:3000
参考答案: node是异步非阻塞的,这对高并发非常有效.可是我们还有其它一些常用需求,比如和操作系统shell命令交互,调用可执行文件,创建子进程进行阻塞式访问或高CPU计算等,child-process就是为满足这些需求而生的.child-process顾名思义,就是把node阻塞的工作交给子进程去做.
参考答案: exec可以用操作系统原生的方式执行各种命令,如管道 cat ab.txt | grep hello; execFile是执行一个文件; spawn是流式和操作系统进行交互; fork是两个node程序(javascript)之间时行交互.
参考答案: 那就用spawn吧.
代码演示
var cp = require('child_process');
var child = cp.spawn('echo', ['你好', "钩子"]); // 执行命令
child.stdout.pipe(process.stdout); // child.stdout是输入流,process.stdout是输出流
// 这句的意思是将子进程的输出作为当前程序的输入流,然后重定向到当前程序的标准输出,即控制台
参考答案: 用fork嘛,上面讲过了.原理是子程序用process.on, process.send,父程序里用child.on,child.send进行交互.
代码演示
1) fork-parent.js
var cp = require('child_process');
var child = cp.fork('./fork-child.js');
child.on('message', function(msg){
console.log('老爸从儿子接受到数据:', msg);
});
child.send('我是你爸爸,送关怀来了!');
2) fork-child.js
process.on('message', function(msg){
console.log("儿子从老爸接收到的数据:", msg);
process.send("我不要关怀,我要银民币!");
});
参考答案: 1) 在myCommand.js文件头部加入 #!/usr/bin/env node 2) chmod命令把js文件改为可执行即可 3) 进入文件目录,命令行输入myComand就是相当于node myComand.js了
参考答案: 概念都是一样的,输入,输出,错误,都是流.区别是在父程序眼里,子程序的stdout是输入流,stdin是输出流.
参考答案: node是单线程的,异步是通过一次次的循环事件队列来实现的.同步则是说阻塞式的IO,这在高并发环境会是一个很大的性能问题,所以同步一般只在基础框架的启动时使用,用来加载配置文件,初始化程序什么的.
参考答案: 1) 多层嵌套回调 2) 为每一个回调写单独的函数,函数里边再回调 3) 用第三方框架比方async, q, promise等
参考答案: 多种方式 1) sudo 2) apache/nginx代理 3) 用操作系统的firewall iptables进行端口重定向
参考答案: 1) runit 2) forever 3) nohup npm start &
参考答案: 一个CPU运行一个node实例
参考答案: 用–max-old-space-size 和 --max-new-space-size 来设置 v8 使用内存的上限
参考答案: 1) node --prof 查看哪些函数调用次数多 2) memwatch和heapdump获得内存快照进行对比,查找内存溢出
参考答案: 1) try-catch-finally 2) EventEmitter/Stream error事件处理 3) domain统一控制 4) jshint静态检查 5) jasmine/mocha进行单元测试
参考答案: node --debug app.js 和node-inspector
参考答案: async是一个js类库,它的目的是解决js中异常流程难以控制的问题.async不仅适用在node.js里,浏览器中也可以使用.
async.parallel([
function(){ ... },
function(){ ... }
], callback);
async.series([
function(){ ... },
function(){ ... }
]);
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
async.map(['file1','file2','file3'], fs.stat, function(err, results){
// results is now an array of stats for each file
});
async.filter(['file1','file2','file3'], fs.exists, function(results){
// results now equals an array of the existing files
});
参考答案: app.js, package.json, bin/www, public, routes, views.
参考答案: express.Router路由组件,app.get路由定向,app.configure配置,app.set设定参数,app.use使用中间件
参考答案: /users/:name使用req.params.name来获取; req.body.username则是获得表单传入参数username; express路由支持常用通配符 ?, +, *, and ()
参考答案: res.download() 弹出文件下载
res.end() 结束response
res.json() 返回json
res.jsonp() 返回jsonp
res.redirect() 重定向请求
res.render() 渲染模板
res.send() 返回多种形式数据
res.sendFile 返回文件
res.sendStatus() 返回状态
参考答案: 类似传统数据库,索引和分区.
参考答案: mongoose是mongodb的文档映射模型.主要由Schema, Model和Instance三个方面组成.Schema就是定义数据类型,Model就是把Schema和js类绑定到一起,Instance就是一个对象实例.常见mongoose操作有,save, update, find. findOne, findById, static方法等.
参考答案: set/get, mset/hset/hmset/hmget/hgetall/hkeys, sadd/smembers, publish/subscribe, expire
参考答案:
var redis = require("redis"),
client = redis.createClient();
client.set("foo_rand000000000000", "some fantastic value");
client.get("foo_rand000000000000", function (err, reply) {
console.log(reply.toString());
});
client.end();
参考答案: 二者都是代理服务器,功能类似.apache应用简单,相当广泛.nginx在分布式,静态转发方面比较有优势.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。