前端基础:

1.讲讲对web标准以及w3c的理解与认识

web可以简单分为:结构、表现、行为。三部分独立开来使其模块化
w3c是对web做出规范,使代码更严谨,做出来的网页更易使用,维护。

w3c做出的规范可分为如下:
结构上:(标签规范对页面的搜索权重有很大关系,写的越规范网站在搜索排名越靠前)

  •   标签闭合、标签小写、不乱嵌套

表现、行为上:

  •  使用外链的css和js脚本,提高页面渲染效率。
  •   少使用行内样式,类名要做到见名知意

2.说明ajax请求的时候post和get的区别

发送机制

1、get请求会将参数跟在URL后面进行参数传递,而post请求则是作为http消息的实体内容发送给web服务器;
2、get提交的数据限制是1024字节,这种显示是来自特定浏览器和服务器对它的限制。如ie的URL长度限制是2083字节,火狐理论上没有长度限制。注意这个限制是URL 的整个长度,而不是参数的长度。
3、get方式请求的数据会被浏览器缓存起来。因为其他人可以从浏览器的历史记录中读取到这些数据,比如:账号或者密码等。在某种情况下,get方式会带来严重的安全问题,而post相对来说可以避免这些问题。

在服务端的区别

1、客户端请求使用get时,服务端使用Request.QueryString来获取,而客户端使用post请求时,服务端使用Request.Form来获取。
2、post用于创建资源,资源的内容会被编入http请示的内容中,例如,处理订货表单等。
3、当请求无副作用时(如进行搜索),使用get方法,当请求有副作用时(如添加数据),则用post方法。

3.如何对网站的文件和资源进行优化,主流的解决方案包括哪些?

1. 文件合并
2. 文件最小化/文件压缩
3. 使用 CDN 托管
4. 缓存的使用(多个域名来提供缓存)
5. 其他

4.闭包是什么?有什么特性?对页面有什么影响?

“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

通俗的讲:就是函数a的内部函数b,被函数a外部的一个变量引用的时候,就创建了一个闭包。

闭包的特性:

①.封闭性:外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口;
②.持久性:一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,闭包结构依然保存在
系统中,闭包中的数据依然存在,从而实现对数据的持久使用。

优点:

① 减少全局变量。

② 减少传递函数的参数量

③ 封装;

   缺点:
 使用闭包会占有内存资源,过多的使用闭包会导致内存溢出等.

5.https页面下直接请求http会遇到什么问题?

在https页面下的带有相对路径的请求都会与页面的协议保持一致。如果想在https页面下发送http的请求,如果只把链接写死成为http的绝对路径是不够的,这样会导致http的请求与总页面https的请求的session不一致。

为什么呢?原因是https的请求中服务器发回的cookie是标记为"secure"的,而http的请求时非"secure","由于在服务器端secure"的cookie不会兼容非"secure"的,所以当http的请求携带着同一jsessionid的cookie到达服务器时,服务器拒绝非"secure",进而返回的结果是一个新的非"secure"的cookie,于是两个session就不同了。

 怎么解决呢?由原因分析可知,两个session不同,更具体说是cookie的状态不同。那么办法是,在接收到第一个https请求的响应后 到 发送下面的http请求之前,将cookie去"secure"状态,但是又要保证jsessionid不变。具体操作可以新建一个cookie(新建的是非"secure"状态),然后赋予同一个jessionid,然后加入response中。

6.js继承的方式及其优缺点

推荐文章:传送门

人家讲得已经很清楚了,案例跟优缺点都整理出来了,推荐阅读

7.解释ajax的工作原理

1、创建ajax对象(XMLHttpRequest/ActiveXObject(Microsoft.XMLHttp))

2、打开链接 open(请求方式,'请求路径',同步/异步)

3、发送 send()

4、当ajax对象完成第四步(onreadystatechange)数据接收完成,判断对象状态码(readystate) 4  HTTP响应完全接收  在判断http响应状态(status)200-300之间或者304(缓存)执行回调函数 获取的数据转成字符串格式(responseText)

具体代码参考网上...

JS部分:

1.打印以下执行结果

console.log('script start');
setTimeout(() => {
    console.log('setTimeout');
},0)
Promise.resolve().then(function() {
    console.log('promise1');
}).then(function() {
    console.log('promise2');
});
console.log('script end')

考察的是代码执行顺序问题,定时器和主程序属于宏任务,Promise中then的回调属于微任务,在每个宏任务里面都会先执行完微任务再去执行下一个宏任务。
本题执行逻辑应该是主线程中console.log('script start');碰到定时器,把这个宏任务扔事件队列里面,继续向下,碰到Promise.then异步任务,继续扔队列里面,执行console.log('script end');再去执行当前宏任务里面的微任务,即Promise.then回调函数,最后执行第二个宏任务定时器.

执行结果:
script start
script end
promise1
promise2
setTimeout

2.打印以下执行结果

const promise = new Promise((resolve,reject) => {
    console.log(1);
    resolve();
    console.log(2);
});
promise.then(()=>{
    console.log(3);
});
console.log(4);

Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的。所以

执行结果:
1
2
4
3

3.打印以下执行结果

var funcs = [];
for(var i=0;i<10;i++) {
    funcs.push(function() {
        console.log(i);
    });
}
funcs.forEach(function(func) {
    func();
});

基础题,匿名函数中的 i 值只有在函数调用执行的时候才会去寻找对应变量的,for循环结束后执行func()时,此时i变量已经变为10(最后有个i++),所以循环打印了十次10,想依次打印0-9的话可以使用立即执行函数解决或者使用ES6的块级作用域let声明变量i.
image.png

4.解释0.1 + 0.2 !== 0.3

js中number类型的数字都是采用64位的双精度浮点数进行存储的,其中1个符号位(0正1负),11个指数位,52个尾数位。0.1用二进制表示如下:
0.000110011001100110011001100110011001100110011001100110011...
如上可以看出0.1二进制表示时尾数是超过52位的, 所以52位之后的会被舍去,这就有了浮点数存储的精度丢失问题。
image.png

5.写出下列执行结果:

setTimeout(()=>{
    console.log('b')
    new Promise((resolve,reject)=>{
        console.log('c');
        resolve()
    }).then(res => {
        console.log('f')
    })
},0);
new Promise((resolve,reject) => {
    console.log('d')
    resolve()
}).then(res => {
    console.log('a')
    setTimeout(()=>{
        console.log('e')
    },0)
})

原理同第一题,答案为:dabcfe

6.写出以下程序运行结果:

let test1 = () => {
    console.log(this);
};
function test2() {
    console.log(this);
};
class t {
    test() {
        console.log(this);
    }
}
let test3 = t.prototype.test;

test1.call(null);
test2.call(null);
test3.call(null);

先给出正确答案:

/// window  ; window ; null  (非严格模式),
/// window  ; null   ; null   (严格模式)

当call或apply的第一个参数为null || undefined时 this指向window ||global

call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象

类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

顺便解释一下call的用法,apply类似,参数不同而已:

call方法

语法:call(thisobj,[argq,arg2])

定义:调用一个对象的一个方法,以另一个对象替换当前对象

说明:

call方法可以用来代替一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始化改为新的对象,也就是括号里面的原本的对象改为call()前面的对象、即用thisobj代替call前面的东西,最终用thisobj这个对象去执行call前面的方法。

如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。 

其实call才是函数调用最原始的方式,现在我们使用的直接myFunc(args)调用函数的方式其实就是call调用的语法糖

7.使用js实现随机选取10–100之间的10个数字,存入一个数组,并排序

let iArray = []; 
function getRandom(istart, iend){
    var iChoice =iend - istart + 1;
    return  Math.floor(Math.random() * iChoice + istart);
}
/*Math.random()就是获取 0-1 之间的随机数(永远获取不到 1)*/
for(let i=0; i<10; i++){
let result= getRandom(10,100);
iArray.push(result);
}
iArray.sort(function(a,b){
 return a>b;
}); 
console.log(iArray);

8.输出以下运行结果:

let foo=1;
function bar(){
    foo=10;
    return;
    functionfoo(){}
}

bar();
console.log(foo); //1
为什么是1而不是10先分析一下每步流程:
第一步:varfoo=1;全局变量foo被初始化赋值成1。
第二步:执行bar();方法。
第三步:bar()方法里,函数声明functionfoo(){}优先处理,这里JavaScript解析语法时(在运行之前)函数优先于一切。所以foo被初始化赋值为function(){};
第四步:执行foo=10;这里制造了一个假象,认为没有用var声明指向的是外层foo=1;。其实不是。而是先在自身函数体里找有没有foo声明,找到之前声明的functionfoo(){};赋值成10,只是局部变量的值改写。
第五步:输出foo,这时找的是全局变量varfoo=1;输出1。

CSS部分:

1.分别写出box的高度

<style>
    .box {
        line-height: 24px;
        background-color: lightblue;
    }
    .box span {
        line-height: 48px;
        border: 1px solid;
    }
</style>
<div class="box">
    <span>content...</span>
</div>

考察的是inline box模型,它的工作就是包裹每行文字,一个没有设置height属性的div的高度就是由一个一个line boxes的高度堆积而成的,撑开div高度的是line-height不是文字内容.MDN 文档中对line-height的描述如下:

line-height CSS 属性用于设置多行元素的空间量, 比如文本。对于块级元素, 它指定元素行盒(line boxes)的最小高度。 对于非替代的inline元素, 它用于计算行盒(line box)的高度。
适用元素 all elements. It also applies to ::first-letter and ::first-line.
如上面最终显示 .box 容器 height = 48px;
1.
将<span>标签里的内容 改为<span>content ... <br> content ... </span> 后,
.box 的height = 96px(48px*2)。
2.
将<span>标签里的内容 改为content ... <br><span> content ... </span> 后,
.box 的height = 48 + 24 = 72px。

2.css实现0.5像素的边框

半像素边框当然不是简单地把1px改为0.5px,浏览器中最小的像素单位为1像素,是不能识别0.5个像素,

1.设置目标元素作为定位参照
box{position:relative}

2.给目标元素添加一个伪元素before或者after,并设置绝对定位
.box:before{content:""; position:absolute;}

3.给伪元素添加1px边框
border:1px solid #000;

4.设置伪元素的宽高为目标元素的2倍
width:200%; height:200%;

5.缩小0.5倍(缩放到原来大小)
transform-origin: 0 0;
transform:scale(0.5,0.5);

6.把border 边框在线框内绘制
box-sizing:border-box;

image.png

Vue框架部分:

1.vue组件中的data为什么必须是个函数

简单地说,对象是引用数据类型,那你每次导入这个组件的时候,其实引用的是同一个内存地址,componentA改变了引用这块地址的数据后,componentB中的这块地址对应的数据也会被改变。
那是因为在js中,函数的{}才有独立的作用域,对象的{},if(){}是不构成作用域的.
函数里面,每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的,这样的话,不管你同时引入同一个组件多少次,他们之间的组件属性都是独立的,互不干扰。

2.解释vue的响应式原理

vue的响应式核心是Object.defineProperty实现的.

Object.defineProperty(obj, key, {
    set:function(){},
    get:function(){}
})

被Object.defineProperty绑定过的数据会被监听到,改变这个对象的时候会触发get和set事件,这也是vue的model层和view层通信的基础,其实vue中的Observer就是基于Object.defineProperty来实现的。
Observer是数据的观察者,与model层直接通信,当数据更改时,它会通知dep(专门管理数据监听依赖的东西),dep会收集到它所需要的依赖,当get的时候,收集订阅者(这里的订阅者其实是观察者模式下的订阅者,简单地说,就是它需要知道具体get什么订阅者,什么是观察者模式和发布订阅者设计模式,具体网上很多相关资料解释),把他添加到依赖,比如,watch和computed都依赖一个data进行监听或者计算,那么dep会将这两个不同的依赖收集。当set的时候会发布更新,通知watcher更新view层。
一个属性可能有多个依赖,每个响应式数据都有一个Dep来管理它的依赖。
每一个依赖又是依靠一个中介的角色来通知变化从而更新视图的,因此watcher能通知变化去执行各自的代码,当然它也能区分自己所属的依赖,比如自己是属于data还是watch还是computed.
放两张网上的图可以更明了的说明他们之间的关系,也可以看看大佬们的文章,讲的更仔细,想学会这种设计模式还是要撸源码,应付面试已经够了。
image.png
原图链接

image.png
原图链接

3.解释v-model的原理

v-model本质上就是一个语法糖,实现原理其实就是上面说过的数据绑定加上底层的input事件监听,通过v-bind绑定一个数据传给子组件,子组件里面的model默认用value属性接受,然后子组件监听数据发生变化,emit触发父组件的input事件,通过触发事件来进行传值,实现了父子组件数据的双向绑定。

4.设计模式-发布订阅者模式

定义了一种一对多的依赖关系,即当一个对象的状态发生改变的时候,所有依赖他的对象都会得到通知。

观察者模式是由具体目标(发布者/被观察者)调度的,而发布/订阅模式是由独立的调度中心进行调度,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会(相当于一个中介的角色,一个全局的Event,发布者不需要知道具体要通知谁,订阅者也不需要知道具体是谁通知的);可以说发布订阅模式是观察者模式进一步解耦,在实际中被大量运用的一种模式。

5.vue.js如何做单元测试?

单元测试:按空间切割,对每个组件进行测试

比如,我要测试日期输入框,那么我编写的测试用例应该包括以下部分:

  • 默认日期是否为当天
  • 当用户选择日期范围,data是否会做相应改变
  • ...

E2E测试:按时间切割,对每个流程进行测试

比如,我要测试搜索功能,那么我编写的测试用例应该模拟以下步骤:

  • 打开主页
  • 点击菜单跳转到详情页
  • 输入搜索条件
  • 点击搜索
  • 查看搜索结果是否与预期一致
  • vue init webpack test

6.vue v-on绑定多个方法

1.v-on绑定多个方法:
<p v-on="{click:dbClick,mousemove:MouseClick}"></p>

2.一个事件绑定多个函数:
<p @click="one(),two()">点击</p>

7.举例vue中常用的修饰符

.lazy:

v-modeil不用多说,输入框改变,这个数据就会改变,lazy这个修饰符会在光标离开input框才会更新数据:

<input type="text" v-model.lazy="value">

.trim:

输入框过滤首尾的空格:

<input type="text" v-model.trim="value">

.number:

先输入数字就会限制输入只能是数字,先字符串就相当于没有加number,注意,不是输入框不能输入字符串,是这个数据是数字:

<input type="text" v-model.number="value">

.stop:

阻止事件冒泡,相当于调用了event.stopPropagation()方法。这个应该不需要解释:

<button @click.stop="test">test</button>

.prevent:

阻止默认行为,相当于调用了event.preventDefault()方法,比如表单的提交、a标签的跳转就是默认事件:

<a @click.prevent="test">test</a>

.self:

只有元素本身触发时才触发方法,就是只有点击元素本身才会触发。比如一个div里面有个按钮,div和按钮都有事件,我们点击按钮,div绑定的方法也会触发,如果div的click加上self,只有点击到div的时候才会触发,变相的算是阻止冒泡:

<div @click.self="test"></div>

.once:

只能用一次,无论点击几次,执行一次之后都不会再执行:

<div @click.once="test"></div>

.capture:

事件的完整机制是捕获-目标-冒泡,事件触发是目标往外冒泡,比如:

<div @click="test(1)">  <button @click="test(2)">test</button></div>

顺序是2 1,capture的作用就是让这个顺序相反:

<div @click.capture="test(1)">  <button @click="test(2)">test</button></div>

先1 后2。

.passive:

其实我不怎么理解,官网解释说可以提升移动端的性能,查了查,大概解释就是每次滚动都会有一个默认事件触发,加了这个就是告诉浏览器,不需要查询,不需要触发这个默认事件preventDefault:

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 --> 

<!-- 而不会等待 `onScroll` 完成 -->

 <!-- 这其中包含 `event.preventDefault()` 的情况 -->

<div v-on:scroll.passive="onScroll">...</div>

.native:

组件绑定当前组件的事件是不会触发的,需要用native才能触发:

<My-component @click="shout(3)"></My-component>

鼠标.left、.reight、.middle:

就是鼠标点击的时候就触发:

<button @click.right="test">test</button>

.keyCode:

监听按键的指令,具体可以查看vue的键码对应表:

<input type="text" @keyup.enter="test(1)">

<button @click.enter="test(1)">test</button>

注意,只有你点击过一次或者聚焦到这个输入框才能使用键盘触发。

.exact:

系统修饰键,只有按着这个键然后用鼠标点击才会触发,官网解释:

<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->

 <button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->

<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

但是我试了一下没有用。

.sync

对prop进行双向绑定,个人暂时用不习惯:

//父组件

<fa-comp :fatest.sync="test"></fa-comp>

//子组件

this.$emit('update:fatest,sontest);

8.scope局部样式的实现原理

当你在单个组件的style标签的内部加上了scoped,在编译的时候就会给当前组件的html标签加上一个data-v-hash的属性并且属性的值是哈希值,我们的样式中用选择器去选到对应的哈希值,从而达到但组件样式的局部化.

9. vue和react当中key的作用

使用key的场景:同级别同类型的节点的时候需要加key
虚拟DOM其实就是把标签的信息,属性,text这些内容以对象的形式储存起来显示
diff算法:在数据发生更新改变的时候,diff算法会对比DOM(对象)前后状态,然后找出差异,只更新差异部分的内容
key的作用除了防止报错以外,写一个类似于无重复的ID的key值让diff算法高效的定位和识别到DOM对象的定位,与DOM对象内容的前后状态是否改变

......

持续更新~

推荐大佬的一篇关于浏览器和js线程的文章,炒鸡棒

webpack篇

1. 谈谈你对webpack的理解?

webpack是一个打包模块化js的工具,在webpack里一切文件皆模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注构建模块化项目。WebPack可以看做是模块的打包机器:它做的事情是,分析你的项目结构,找到js模块以及其它的一些浏览器不能直接运行的拓展语言,例如:Scss,TS等,并将其打包为合适的格式以供浏览器使用。

2.说说webpack与grunt、gulp的不同?

三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。

grunt和gulp是基于任务和流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
所以,从构建思路来说,gulp和grunt需要开发者将整个前端构建过程拆分成多个Task,并合理控制所有Task的调用关系;webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工
对于知识背景来说,gulp更像后端开发者的思路,需要对于整个流程了如指掌 webpack更倾向于前端开发者的思路

3.什么是bundle,什么是chunk,什么是module?

  • bundle:是由webpack打包出来的文件
  • chunk:代码块,一个chunk由多个模块组合而成,用于代码的合并和分割
  • module:是开发中的单个模块,在webpack的世界,一切皆模块,一个模块对应一个文件,webpack会从配置的entry中递归开始找出所有依赖的模块

4.什么是Loader?什么是Plugin?

1)Loaders是用来告诉webpack如何转化处理某一类型的文件,并且引入到打包出的文件中
2)Plugin是用来自定义webpack打包过程的方式,一个插件是含有apply方法的一个对象,通过这个方法可以参与到整个webpack打包的各个流程(生命周期)。

5.有哪些常见的Loader?他们是解决什么问题的?

  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
  • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试
  • image-loader:加载并且压缩图片文件
  • babel-loader:把 ES6 转换成 ES5
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
  • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
  • eslint-loader:通过 ESLint 检查 JavaScript 代码

MangoGoing
774 声望1.2k 粉丝

开源项目:详见个人详情