前端中高级基础知识面试汇总
Posted Smile沛沛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端中高级基础知识面试汇总相关的知识,希望对你有一定的参考价值。
持续更新ing~
- 前端基础github地址。
README.md
可以下载到typora
中打开,会有整个大纲目录显示(github中markdown目录快捷生成方式不现实,之后可能会想办法生成贴过来,暂时不做相关处理) - 前端基础gitbook地址。
README.md
中会实时更新进度内容。gitbook中考虑整个学完整理完成之后,再去统一处理发布,敬请期待!gitbook
版本可建议后期碎片化时间进行复习使用。 - 前端基础csdn地址。
CSDN
博客专栏前端自我修养进阶中,也会一篇一篇实时更新相关知识点。 - 前端基础一掘金地址、前端基础二掘金地址
⭐️就是最好的鼓励哦wink~💗
4.27
html5
-
标签语义化
- 合适的标签做合适的事情
- 标签分类:
- 块状标签
- 独占一行
- example:div,h1-h6,hr,table,form,p,li,dl,dt
- 行内标签
- example:span、a、img
- 行内块状标签
- example:input
- 块状标签
-
音视频
- 改变了传统的flash播放,采用video和audio等
-
webGL/canvas等动画渲染
-
websocket:
- 改变了传统的长轮询方式
css3
-
盒子模型
- 标准盒模型
- width和height只是指内容content部分,不包括padding、border、margin部分
- IE盒模型(怪异盒模型)
- width和height包括margin、border、padding的盒子
- 通常我们使用的都是标准盒模型,但是我们有些情况下需要使用IE盒模型的话可以设置
box-sizing:border-box
- 标准盒模型
-
水平居中的实现方式
- 使用定位
.container{ position:relative; } .box{ width:200px; height:200px; position:absolute; left:50%; top:50%; margin-left:-100px; margin-right:-100px; } //或者 .box{ position:absolute; left:50%; top:50%; transformX:-50%; transformY:-50%; }
- 使用flex
.container{ display:flex; justify-content:center; align-item:center; }
- 使用table
.container{ display:table-ceil; text-align:center; vertical-align:middle; width:500px; height:500px; } .box{ display:inline-block; }
- 使用JS控制
-
响应式布局
- 圣杯布局
- 利用position定位以及浮动还有负margin进行实现
- 双飞翼布局
- flex实现圣杯布局
- 圣杯布局
.container{
display:flex;
flex-direction:row;
justify-content:space-between;
}
.left{
height:200px;
flex:0 0 200px; //缩放比0 0,宽度200px
}
.center{
flex:1; //或者使用flex-grow
}
.right{
height:200px;
flex:0 0 200px;
}
布局方案
- media媒体查询
- 适用于一套代码多端适配
- rem适用于移动端
- vm/vh百分比布局
- flex
z-index原理
- 使元素脱离文档流
- 使元素脱离文档流的其他的方式:
- 浮动
- 定位
- transform
- Css3动画
不考虑其他因素下面哪种渲染性能比较高
//方式一
.box a{
}
//方式二
a{
}
方式二:因为浏览器的渲染机制使从右到左进行查找的
js的数据类型
- 基本类型:
- number
- string
- boolean
- null
- undefined
- 引用类型
- object
- function
- 特殊类型
- Symbol
判断数据类型的几种方式
- typeof
- instanceof
- constructor
- Object.prototype.toString.call
4.28
堆栈内存、闭包作用域
let a = {},b = '0',c = 0;
a[b] = '张三'
a[c] = '李四'
console.log(a[b]) //李四
对象中,数字属性名和字符串属性名都是一样的,数组是特殊的字符串
拓展:对象和数组的区别
let a={}, b=Symbol('1'), c=Symbol('1');
a[b]='张三';
a[c]='李四';
console.log(a[b]); //张三
Symbol创建的是唯一的值,不相等。对象的属性名不一定是字符串,如果是数字和字符串则是同一个值,因为索引是字符串。对象的属性名可以是不二null,Symbol、undefined等等。引用类型值都是变成字符串逆行存储。
拓展:自己实现一个Symbol
let a={}, b={n:'1'}, c={m:'2'};
a[b]='张三';
a[c]='李四';
console.log(a[b]); //李四
b、c作为引用的时候会被转化成为字符串,对象转化toString那么即
[Object Object]
,两个都是[Object Object]
,所以结果是李四
拓展:Object.prototype.toString项目中应用,跟valueOf跟toString区别(编译顺序)
基本类型直接存储,引用类型要放进堆里面,最终是把地址复制给这个值。
浏览一加载页面,就形成一个栈内存。每个函数执行,叫做把一个执行上下文压缩到栈中执行。
null和undefined
//立即执行函数,执行完被销毁,回收机制。但是由于i被占用因此不销毁。test表示的不是一个函数,而是一个函数返回的执行结果
var test = (function(i){
return function(){
alert(i*=2); //没有i,那么在它上级作用域中找。在哪创建的上级作用域就是谁
}
})(2);
test(5); //字符串4。test中没有形参。
alert输出的结果都是会转化成字符串的。
var a=0,
b=0;
function A(a){
A=function(b){
alert(a+b++);
};
alert(a++);
}
A(1); //"1"
A(2); //"4"
注意: a++先跟别人运算,再自身累加1;++a先自身累加1,再跟别人运算。
过程解释:
1、GO全局:初始化 a = 0,b = 0, A一个方法,引用地址,此处暂时记为AAAFFF000
2、A(1)执行,传入形参,此时在执行中a= 1,往下中性,A = function…,此处相当于重新定义了全局的方法A,改变了原来的方法A的指向,此处引用地址从AAAFFF000改变到另外一个记为BBBFFF000,继续执行alert(a++),由于a=1,并且由于下面demo可以看出,a是在执行之后再自身加1的,那么此时alert的是没有叠加之前的1
3、继续执行A(2),那么此时A指向的function是改变之后的BBBFFF000,此时传入的形参b=2,而a在其上一级作用域中,由步骤2可知,叠加之后为2,那么此时alert出来的应该是2+2,为字符串4(alert出来的会自动同toString转化)
let a = 1;
console.log(5+a++); //6
console.log(a) //2
let b = 1;
console.log(5+(++a)); //7
console.loog(a) //2
闭包的作用:保存、保护
对象和数组的深浅拷贝
let obj = {
a: 100,
b: [10, 20, 30],
c: {
x: 10
},
d: /^\\d+$/
};
let arr = [10, [100, 200], {
x: 10,
y: 20
}];
//=>深克隆
function deepClone(obj) {
if (typeof obj !== "object") return obj;
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);
let cloneObj = new obj.constructor;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
}
- 浅拷贝:只拷贝第一层,操作第二级仍然可以改第一级。只是浅拷贝,只拷贝引用
let obj = {
a: 100,
b: [10, 20, 30],
c: {
x: 10
},
d: /^\\d+$/
};
let obj2 = {}
for(let key in obj){
//obj.hasOwnProperty(key),true是私有的,false不是私有的。不是私有的不遍历,只遍历私有的
if(!obj.hasOwnProperty(key))break;
obj2[key] = obj[key]
}
//ES6实现浅克隆
let obj3 = {...obj}
- 深拷贝:(递归/字符串转化)
// JSON.stringify&JSON.parse
let obj = {
a: 100,
b: [10, 20, 30],
c: {
x: 10
},
d: /^\\d+$/
};
let obj2 = JSON.stringify(obj); //"{"a":100,"b":[10,20,30],"c":{"x":10},"d":{}}"
obj2 = JSON.parse(obj2)
JSON.parse
这种方式弊端:正则、函数、日期、symbol等,会有问题
//递归实现深克隆
function deepClone(obj){
//或者let newobj = new obj.constructor;或者{};一般obj.constructor为Object,防止传入的是实例
//不直接创建控对象的目的,克隆的结果和之前保持相同的所属类
//过滤特殊情况,object才递归
if(typeof obj === null)return null;
if(typeof obj !== "object")return obj;
if(obj instanceOf RegExp){
return new RegExp(obj)
}
if(obj instanceOf Date){
return new Date(obj)
}
let newobj = new Object();
for(let key in obj){
if(obj.hasOwnProperty(key)){ //判断是私有属性
newobj[key] = deepclone(obj[key ])
}
}
return newobj;
}
排除:null,Date,正则RegExp,以及不是object
面向对象
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName(); //2
getName(); //4
Foo().getName(); //Foo()普通函数执行;Foo执行过程中,内部函数有个getName赋值,但是getName并不是私有的,于是此时重新定义全局的getName = -> 1,并且返回this,这里的this的指向就是window,那么应该是window.getName 1
getName(); //1
new Foo.getName(); //无参数new,点的方式叫做成员访问。优先级问题,先执行成员访问,new一个输出2的函数,那么输出也是2
new Foo().getName(); //有参数new,执行方式先new Foo,再getName。创建实例,实例的getName,那么应该找prototype上的,因此结果是3
new new Foo().getName(); //优先级new (new Foo()).getName().getName,先new Foo()创建一个实例foo,变成 new foo.getName(),此时变成了先成员访问,原型上的方法,输出3的方法,变成了new 3,即3
注意:由参数的new先执行new,无参数的new和成员访问两个同级别,谁先救先执行谁
答案为:2 4 1 1 2 3 3
//function声明加赋值,var先声明先不赋值
Foo AAAFFF0000
getName = func -> 5
//代码执行
Foo是一个堆,里面存了方法代码字符串,Foo仍然是一个对象,有prototype(也是一个引用类型即也是一个堆)、length、然后我们给他加了一个getName属性;
getName = func -> 4 //之前赋值输出5,之后重新赋值输出4的
变量提升:在var function,所有代码执行之前,带var和function提前定义和赋值
同步异步EventLoop
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
浏览器多线程,js单线程。时间队列EventQueue。微任务队列,宏任务队列。主线程代码先执行。
定时器、事件绑定、ajax都是宏任务,async、await、promise等是微任务
4.29
- 题一:
function A(){
alert(1);
}
function Func() {
A=function(){
alert(2);
};
return this;
}
Func.A=A;
Func.prototype={
A:()=>{
alert(3);
}
};
A();
Fn.A();
Fn().A();
new Fn.A();
new Fn().A();
new new Fn().A();
//注意最后一个,箭头函数是不可以被new的,因为箭头函数没有原型链,也就没有constructor构造器
- 题三:
var a = ?;
if (a == 1 && a == 2 && a == 3) {
console.log(1);
}
两个等于号转化数据类型值相等,三个等于好要绝对相等
双等
- 对象==字符串 对象toString变成字符串,再变成数字,对比;[10] == 10
- null == undefined但是和其他比较不想等
- NaN和任何东西都不想等
- 剩下的都转化成数字。eg.
'1'==true
1、利用toString
由于两个等号对比,一方为数字,我们假设a为object(例如:[1],[2],[3]这样的,数组是特殊的object),那么他们执行的对比方式一定是要先toString再转化成数字进行对比的。因此我们可以在toString上做文章,去修改它的toString
var a = {
i:0,
toString(){
return ++this.i;
}
}
//因此三次对比,执行三次toString,依次返回1,2,3。valueOf也可以代替toString
var a= [1,2,3]
a.toString = a.shift; //shift方法删除数组第一个值,并且返回第一个数值。因此每次对比要调用a的toString的时候,依次获得的数值就是i,2,3
2、利用数据劫持get:
由于a是全局的,那么再全局上去劫持获取a的set方法去修改
var i = 0;
Object.definedProperty(window,'a',{
set(){
return ++i;
}
})
3、或者用true也行
Vue&React
-
react和vue区别
- vue代表MVVM(双向数据绑定)。
- 数据更改视图变化,视图更改数据变化
- React代表MVC。
- vue吧给我们把表单绑定事件写好了,React需要自己去绑定
- React中需要自己去实现onchange事件
- vue中直接v-mode直接帮我们绑定了数据,不用自己写onChange事件
- MVC和MVVM区别
- MVC默认值实现数据的更改(单向数据改变)
- MVVM实现数据更改视图更改,视图更改数据更改(双向数据改变)
- MVC这种只是缺少一个onChange事件绑定
-
vue中数据的双向绑定2.0和3.0的实现
- vue2.0中,通过
Object.definedProperty
进行set和get修改; - vue3.0中通过
Proxy
进行拦截set和get方法进行相关操作,实现数据的双向绑定。
- vue2.0中,通过
跨域问题的解决方案和实现原理
1、JSONP
利用script标签
- 只有get请求
- 不安全
- 有缓存
- 传递的数据大小有限制
- 需要服务器单独的支持
- 客户端
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
$.ajax({
url: 'http://127.0.0.1:8001/list',
method: 'get',
dataType: 'jsonp',
success: res => {
console.log(res);
}
});
</script>
- 后台
let express = require('express'),
app = express();
app.listen(8001, _ => {
console.log('OK!');
});
app.get('/list', (req, res) => {
let {
callback = Function.prototype
} = req.query;
let data = {
code: 0,
message: '测试数据'
};
res.send(`${callback}(${JSON.stringify(data)})`);
});
2、基于iframe的跨域解决方案
主域相同,子域不一样
- window.name
- document.domin
- location.hash
- post message
3、CORS跨域资源共享
客户端
import axios from 'axios';
import qs from 'qs';
axios.defaults.baseURL = "http://127.0.0.1:3000";
axios.defaults.timeout = 10000;
axios.defaults.withCredentials = true;
/*
* 设置请求传递数据的格式(看服务器要求什么格式)
* x-www-form-urlencoded
*/
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
axios.defaults.transformRequest = data => qs.stringify(data);
/*
* 设置请求拦截器
* TOKEN校验(JWT):接收服务器返回的token,存储到vuex/本地存储中,每一次向服务器发请求,我们应该把token带上
*/
axios.interceptors.request.use(config => {
let token = localStorage.getItem('token');
token && (config.headers.Authorization = token);
return config;
}, error => {
return Promise.reject(error);
});
/*
* 响应拦截器
*/
axios.interceptors.response.use(response => {
return response.data;
}, error => {});
export default axios;
服务器
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "";
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Headers", "PUT,POST,GET,DELETE,OPTIONS,HEAD");
res.header("Access-Control-Allow-Methods", "Content-Type,Content-Length,Authorization, Accept,X-Requested-With");
req.method === 'OPTIONS' ? res.send('CURRENT SERVICES SUPPORT CROSS DOMAIN REQUESTS!') : next();
});
4、基于http proxy实现跨域请求
开发时候用proxy,部署用nginx反向代理
Vue/React框架中关于组件信息通信引发的面试题
vue
- 属性传递
- 发布订阅(EventBus):$on / $emit
- Provide / inject
- slot插槽
- $parent / $children
- vuex
react
- 属性
- 发布订阅
- React.createContext
- redux / react-redux /
以上是关于前端中高级基础知识面试汇总的主要内容,如果未能解决你的问题,请参考以下文章