面试题
html5 css3
1. H5新特性
- 拖拽释放(Drag and drop) API
- 语义化更好的内容标签(header,nav,footer,aside,article,section)
- 音频、视频API(audio,video)
- 画布(Canvas) API
- 地理(Geolocation) API
- 数据存储 localStorage、sessionStorage
- 表单控件 calendar、date、time、email、url、search
- 语义化标签的优点
-
代码结构清晰,易于阅读,利于开发和维护
-
提高用户体验,在样式加载失败时,页面结构清晰
-
方便其他设备解析(如屏幕阅读器)根据语义渲染网页。
-
有利于搜索引擎优化(SEO),搜索引擎爬虫会根据不同的标签来赋予不同的权重
2. CSS3新特性
- 颜色:新增RGBA,HSLA模式
- 文字阴影(text-shadow)
- 边框: 圆角(border-radius)边框阴影: box-shadow
- 盒子模型:box-sizing
- 背景:background-size 设置背景图片的尺寸;background-origin 设置背景图片的原点;background-clip 设置背景图片的裁切区域,以”,”分隔可以设置多背景,用于自适应布局
- 渐变:linear-gradient、radial-gradient
- 过渡:transition,可实现动画
- 自定义动画
- 在CSS3中唯一引入的伪元素 :selection.
- 媒体查询,多栏布局
- border-image
- 2D转换:transform:translate(x,y) rotate(x,y) skew(x,y) scale(x,y)
- 3D转换
- 新增选择器:属性选择器、伪类选择器、伪元素选择器。
2.1 清除浮动
-
使用after伪元素清除浮动
给浮动元素的容器添加一个clearfix的class,然后给这个class添加一个::after伪元素实现元素末尾添加一个看不见的块元素(Block element)清理浮动。
.clearfix::after { /*伪元素是行内元素 正常浏览器清除浮动方法*/
content: "";
clear: both;
display: table;
}
.clearfix{
*zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行
}
<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
</div>
-
使用before和after双伪元素清除浮动
给浮动元素的容器添加一个clearfix的class
.clearfix::before,
.clearfix::after{
content: '';
display: table;
clear: both;
}
- 额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐)
.clear{
clear:both;
}
<div class="fahter">
<div class="big">big</div>
<div class="small">small</div>
<div class="clear">额外标签法</div>
</div>
- 父级添加overflow属性(父元素添加overflow:hidden)(不推荐)
.fahter{
overflow: hidden;
}
3. ES6新特性
-
不一样的变量声明:const和let
-
模板字符串 ``
-
箭头函数(Arrow Functions)
-
Spread / Rest 操作符
Spread / Rest 操作符指的是 …,具体是 Spread 还是 Rest 需要看上下文语境。
// Spread
function foo(x,y,z) {
console.log(x,y,z);
}
let arr = [1,2,3];
foo(...arr); // 1 2 3
// Rest
function foo(...args) {
console.log(args);
}
foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
- 对象和数组解构
// 对象
const student = {
name: 'Sam',
age: 22,
sex: '男'
}
// 数组
// const student = ['Sam', 22, '男'];
// ES5;
const name = student.name;
const age = student.age;
const sex = student.sex;
console.log(name + ' --- ' + age + ' --- ' + sex);
// ES6
const { name, age, sex } = student;
console.log(name + ' --- ' + age + ' --- ' + sex);
- for…of 和 for…in
for…of 用于遍历一个迭代器,如数组:
let letters = ['a', 'b', 'c'];
letters.size = 3;
for (let letter of letters) {
console.log(letter);
}
// 结果: a, b, c
for…in 用来遍历对象中的属性:
let stus = ["Sam", "22", "男"];
for (let stu in stus) {
console.log(stus[stu]);
}
// 结果: Sam, 22, 男
js
1.闭包
1.1. 概念:
闭包是指有权访问另一个函数作用域中的变量的函数
1.2. js链式作用域:
子对象会一级一级向上寻找所有父对象的变量,反之不行。js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。
1.3. js变量的两种作用域:
全局变量、局部变量(函数内):js中函数内部可以读取全局变量,函数外部不能读取函数内部的局部变量。
1.4. 闭包为什么可以实现在函数外读取到函数内的变量?
function f1(){
var n = 123;
function f2(){ //f2是一个闭包
alert(n)
}
return f2;
}
闭包:f2可以读取f1中的变量,只要把f2作为返回值,就可以在f1外读取f1内部变量
原因:f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。
2. apply 和 call
2.1. apply 和 call 的用法
改变 this 指向
var obj = {
name: 'linxin'
}
function func() {
console.log(this.name);
}
func.call(obj); // linxin
2.2. apply 和 call 的区别
改变 this 指向。作用一模一样,只是传参的形式有区别而已。
apply()
apply 方法传入两个参数:一个是作为函数上下文的对象,另外一个是作为函数参数所组成的数组。
var obj = {
name : 'linxin'
}
function func(firstName, lastName){
console.log(firstName + ' ' + this.name + ' ' + lastName);
}
func.apply(obj, ['A', 'B']); // A linxin B
可以看到,obj 是作为函数上下文的对象,函数 func 中 this 指向了 obj 这个对象。参数 A 和 B 是放在数组中传入 func 函数,分别对应 func 参数的列表元素。
call( )
call 方法第一个参数也是作为函数上下文的对象,但是后面传入的是一个参数列表,而不是单个数组。
var obj = {
name: 'linxin'
}
function func(firstName, lastName) {
console.log(firstName + ' ' + this.name + ' ' + lastName);
}
func.call(obj, 'C', 'D'); // C linxin D
3. this
解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的 上下文对象。
根据函数的调用方式的不同,this会指向不同的对象
3.1. this 的指向问题
普通函数:this指向分为4种情况
- obj.getName();//指向obj
- 2.getName();//非严格模式下,指向window,严格模式下为undefined
- var a = new A();
a();//指向A本身 - 4.getName().apply(obj);//指向obj
箭头函数:箭头函数本身是没有this和arguments的,在箭头函数中引用this实际上是调用的是定义时的上一层作用域的this。
这里强调的是上一层作用域,是因为对象是不能形成独立的作用域的。
var obj = {
say: function() {
var f1 = ()=>{
console.log("1111",this);
}
f1();
}
}
var o = obj.say;
o();//f1执行时,say函数指向window,所以f1中的this指向window
obj.say();//f1执行时,say函数指向obj,所以f1中的this指向obj;
var ojb = {
pro: {
getPro: ()=>{
console.log(this);
}
}
}
obj.pro.getPro();//this指向的是window,因为箭头函数定义时,getPro的上一级是pro,是一个对象,不能形成单独的作用域,故指向window。
4. Array 有多少种常用方法
4.1. 改变原数组的方法
1. pop()
语法:arrayObject.pop()
功能:用于删除并返回数组的最后一个元素
返回值:被删除的元素;如果是空数组则不改变数组,返回undefined
// arrayObject.pop()
var arr = [1,2,3];
console.log('arr: '+ arr); // 1,2,3
console.log('the pop element: '+ arr.pop()); // 3
console.log('arr: '+ arr); // 1,2
2. push()
语法:arrayObject.push(newelement1,newelement2,…,newelementX)
功能:想数组的末尾添加一个或多个元素
返回值:把指定的值添加到数组之后的新长度
// arrayObject.push(newelement1,newelement2,....,newelementX)
var arr = [1,2,3];
console.log('arr: '+ arr); // 1,2,3
console.log('the new length: '+ arr.push(4,5)); // 5
console.log('arr: '+ arr); // 1,2,3,4,5
3. shift()
语法:arrayObject.shift()
功能:把数组的第一个元素从其中删除
返回值:数组原来的第一个元素的值
// arrayObject.shift()
var arr = [1,2,3];
console.log('arr: '+ arr); // 1,2,3
console.log('the shift element: '+ arr.shift()); // 1
console.log('arr: '+ arr); // 2,3
4. unshift()
语法:arrayObject.unshift(newelement1,newelement2,…,newelementX)
功能:向数组的开头添加一个或多个元素
返回值:返回数组的新长度
// arrayObject.unshift(newelement1,newelement2,...,newelementX)
var arr = [1,2,3];
console.log('arr: '+ arr); // 1,2,3
console.log('the new length: '+ arr.unshift(14,5)); // 5
console.log('arr: '+ arr); // 14,5,1,2,3
5. splice()
语法:arrayObject.splice(index,howmany,item1,…,itemX)
功能:向数组添加项目或者从数组中删除项目
返回值:向/从数组中添加/删除项目,然后返回被删除的项目
参数:
第一个,表示开始位置的索引
第二个,表示删除的数量
第三个及以后。。
可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
// arrayObject.splice(index,howmany,item1,.....,itemX)
var arr = [1,2,3];
console.log('arr: '+ arr); // 1,2,3
console.log('delete element: '+ arr.splice(0,1)); // 1
console.log('arr: '+ arr); // 2,3
console.log('do not delete element: '+ arr.splice(0,0,6));
console.log('arr: '+ arr); // 6,2,3
6. reverse()
语法: arrayObject.reverse()
功能:用于颠倒数组中的元素的顺序
返回值:返回颠倒顺序之后的数组
7. sort()
语法:arr.sort() 或者 arr.sort(compareFunction)
输入值;无 或者 函数(用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序)
功能:排序
返回值:排序之后的数组
// arr.sort()
// arr.sort(compareFunction)
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
return a - b;
});
console.log(numbers); // [1,2,3,4,5]
4.2. 不改变原数组的方法
1. concat()
语法:arrayObject.concat(arrayX,arrayX,…,arrayX)
输入值: 输入值可以是单个元素(多个元素),也可以是单个数组(多个数组)
功能:连接两个或多个数组
返回值:返回被连接数组的一个副本
// arrayObject.concat(arrayX,arrayX,......,arrayX)
var arr = [1,2,3];
var arr2 = arr.concat(4,5,6);
console.log("arr: " + arr); // 1,2,3
console.log("arr2: " + arr2); // 1,2,3,4,5,6
var arr3 = arr.concat([7,8]);
console.log("arr: " + arr); // 1,2,3
console.log("arr3: " + arr3); // 1,2,3,7,8
var arr4 = arr.concat([9,10],[11,12]);
console.log("arr: " + arr); // 1,2,3
console.log("arr4: " + arr4); // 1,2,3,9,10,11,12
2. join()
语法:arrayObject.join(separator)
输入值:分隔符
功能:把数组中的所有元素放入一个字符串中
返回值:返回一个字符串。该字符串是通过把 arrayObject 的每个元素转换为字符串,然后把这些字符串连接起来,在两个元素之间插入 separator 字符串而生成的。
// arrayObject.join(separator)
var arr = new Array(3);
arr[0] = "A";
arr[1] = "B";
arr[2] = "C";
console.log('arr.join(): ' + arr.join()); // A,B,C
console.log('arr.join(-): ' + arr.join('-')); // A-B-C
3. slice()
语法:arrayObject.slice(start,end)—包括start但不包括end,没有找到元素,则返回空的数组
输入值:
返回值:返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素
4. toString()
语法:booleanObject.toString()
功能:把一个逻辑值转换为字符串,并返回结果
返回值:根据原始布尔值或者booleanObject对象的值返回字符串"true"或“false”
5. const let var 的区别
- var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
- let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
- const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
6. split与join的区别
- split()用于分割字符串,返回一个数组
var string=“hello world?name=xiaobai”;
var splitString = string.split("?");
console.log(splitString);//["hello world","name=xiaobai"]
split()只有一个参数时:分割的字符串或正则表达式;两个参数时,第二个参数是指返回的数组中元素的个数;
- join()用于连接多个字符或字符串,返回值为一个字符串
var arr= new Array();
arr[0]="hello";
arr[1]="xiao";
arr[2]= "bai";
arr.join("&");//"hello&xiao&bai"
join();//默认分割符为逗号;
7. 创建随机数
可用数学方法Math.round()和Math.random()
Math.round(x) 的返回值是 x 四舍五入为最接近的整数
Math.floor(x) 的返回值是 x 下舍入最接近的整数
Math.floor(2.7); // 返回 2
Math.random() 返回 0(包括) 至 1(不包括) 之间的随机数
Math.floor(Math.random() * 10); // 返回 0 至 9 之间的数
Math.floor(Math.random() * 11); // 返回 0 至 10 之间的数
Math.floor(Math.random() * 101); // 返回 0 至 100 之间的数
8. 正则表达式
手机号 /^1[3456789]\d{9}$/
- 使用方法
使用 | 表示或者的意思 reg = /a|b|c/;
[ ] 里的内容也是或的关系
[ab] == a|b
i 执行对大小写不敏感的匹配
g 执行全局匹配
[a-z] 任意小写字母
[A-Z] 任意大写字母
[A-z] 任意字母
[0-9] 任意数字
reg = /[A-z]/;
//检查一个字符串中是否含有 abc 或 adc 或 aec reg = /a[bde]c/;
[^ ] 除了 reg = /[ ^ab]/;
^ 表示开头
$ 表示结尾
{n} 正好出现n次
{m,n} 出现m-n次
{m,} m次以上
9. 基本数据类型
js一共有六种数据类型:五种简单数据类型和一种复杂数据类型:
五种简单数据类型包括:String、Number、Boolean、undefined、Null
一种复杂数据类型:obeject
vue
1. vue-router
1.1. vue-router 中的导航钩子
vue-router 的导航钩子,主要用来作用是拦截导航,让他完成跳转或取消。
三种方式可以植入路由导航过程中:全局的、单个路由独享的、组件级的
1.1.1. 全局导航钩子: 前置守卫、后置钩子
-
注册一个全局前置守卫
const router = new VueRouter({ ... }); router.beforeEach((to, from, next) => { // do someting });
这三个参数 to 、from 、next 分别的作用:
- 1.to: Route,代表要进入的目标,它是一个路由对象
- 2.from: Route,代表当前正要离开的路由,同样也是一个路由对象
- 3.next: Function,这是一个必须需要调用的方法,而具体的执行效果则依赖 next 方法调用的参数
**注意:**next 方法必须要调用,否则钩子函数无法 resolved
-
全局后置钩子
router.afterEach((to, from) => { // do someting });
1.1.2. 路由独享的钩子
即单个路由独享的导航钩子,它是在路由配置上直接进行定义的
cont router = new VueRouter({
routes: [
{
path: '/file',
component: File,
beforeEnter: (to, from ,next) => {
// do someting
}
}
]
});
至于他的参数的使用,和全局前置守卫是一样的
1.1.3. 组件内的导航钩子
组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。他们是直接在路由组件内部直接进行定义的
const File = {
template: `<div>This is file</div>`,
beforeRouteEnter(to, from, next) {
// do someting
// 在渲染该组件的对应路由被 confirm 前调用
},
beforeRouteUpdate(to, from, next) {
// do someting
// 在当前路由改变,但是依然渲染该组件是调用
},
beforeRouteLeave(to, from ,next) {
// do someting
// 导航离开该组件的对应路由时被调用
}
}
2. v-model 的使用和原理
<textarea name="" id="" cols="30" rows="10" v-model="message"></textarea>
<p>输入的内容是:{{message}}</p>
v-model原理
v-model其实是一个语法糖,它的背后本质上是包含两个操作:
1.v-bind绑定一个value值
2.v-on指令给当前元素绑定input事件
3. vue 响应式原理
vue在实例化的过程中,会遍历实例化对象中的data选项,使用Object.defineProperty把这些属性全部转为 getter 和 setter
同时每一个实例对象都有一个watcher实例,在模板编译过程中,getter 会访问 data 属性,watcher 会将用到的 data 属性标记为依赖,这样就建立了视图与数据的联系。当渲染视图的数据依赖发生改变(即setter被调用)的时候,watcher 会对比两个数值是否发生变化,然后确定是否通知视图进行重新渲染。
综合
1. 深拷贝和浅拷贝
浅复制只是复制了堆地址,两个一样的堆地址指向同一个堆内存。数据改变都会改变。
深复制将堆内存复制了一遍,有两个堆地址指向两个堆内存。数据改变互不相关。
1.1. 浅拷贝
var arr = [1,2,3,4,5]
// 浅复制
var arr1 = arr;
console.log(arr1 === arr);
arr[0] = 0
console.log(arr + '---' +arr1); // 0,2,3,4,5---0,2,3,4,5
因为浅复制只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,所以浅复制会导致 obj.arr 和 shallowObj.arr 指向同一块内存地址
浅复制只是复制了堆地址,两个一样的堆地址指向同一个堆内存。
1.2. 深拷贝 JSON.parse(JSON.stringify())
JSON.stringify() 将value(Object,Array,String,Number…)序列化为JSON字符串
JSON.parse() 将JSON数据解析为JS原生值。
var arr = [1,2,3,4,5]
// 深复制
var arr1 = JSON.parse(JSON.stringify(arr))
arr[0] = 0
console.log(arr + '---' +arr1); // 0,2,3,4,5---1,2,3,4,5
而深复制则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。这就不会存在上面 obj 和 shallowObj 的 arr 属性指向同一个对象的问题。
深复制将堆内存复制了一遍,有两个堆地址指向两个堆内存
2. 同步与异步
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
3. 对 MVC、MVVM 的理解
3.1. MVVM 特点:
- View <->ViewModel <=> Model
(1)各部分之间的通信,都是双向的;
(2)采用双向绑定: View 的变动,自动反映在 ViewModel,反之亦然。
- MVVM优点
- 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变
- 可重用性:你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码
- 可测试:界面素来是比较难于测试的,而现在测试可以针对ViewModel来写
3.2. MVC 特点:
- view -> controller -> model -> view
(1)View 传送指令到 Controller;
(2)Controller 完成业务逻辑后,要求 Model 改变状态;
(3)Model 将新的数据发送到 View,用户得到反馈。
所有通信都是单向的。
4. Vue 的核心是什么
数据驱动、组件系统。
- 数据驱动,也就是数据的双向绑定
5. 如何解决跨域问题
- 通过 jsonp 跨域
- proxy反向代理跨域
- CORS
- nodejs中间件代理跨域
- nginx代理跨域
6. 谈谈对原型链的理解
js对象通过prototype指向父类对象,直到object对象,这样就形成了一个原型指向的链条,即原型链。
原型链就是创建一个构造函数,它会默认生成一个prototype属性并指向原型对象。使用下一个构造函数的原型对象作为这个构造函数的实例。
keep-alive用于保存组件的渲染状态。
组件是可以复用的Vue实例。
7. get和post请求的区别
- get请求的参数在url地址栏中,而post将参数放置在HTML HEADER内
- get传送的数据量较小,不能大于2KB。post 传送的数据量较大,一般被默认为不受限制,可自己设置
- get不如post安全性高
8. 网页渲染的全过程
- 输入网址
- 发送到DNS服务器,并获取域名对应的web服务器对应的ip地址
- 与web服务器建立tcp连接
- 浏览器向web服务器发送http请求
- web服务器响应请求,并返回指定url的数据
- 浏览器下载web服务器返回的数据并解析html源文件
- 解析HTML文件,创建DOM树
- 解析CSS,形成CSS对象模型
- 将CSS与DOM合并,构建渲染树(rendering tree)
- 布局和绘制
9. 前端性能优化
- 减少http请求次数
- 使用CDN加载静态资源
- 压缩静态资源
- 减少cookie大小
- 将CSS样式置顶
- 将js脚本置底
- 不要在HTML中缩放图片
- 减少DOM访问
- 用link代替@import