前言
快要开始面试了,在这里记录各种前端知识点,方便自己复习。每一个问题下不止记录该问题的答案,还会记录所有相关的知识点。欢迎大家一起交流学习。
变量类型和计算
1.typeof能判断哪些类型?
值类型:undifined、string、number、布尔、symbol(ES6)
引用类型:对象、数组、null(特殊,指针指向空地址)、函数
typeof可以识别所有的值类型,识别函数,但是只能判断是否为引用类型object(不可细分)
手写实现深拷贝:
function deepClone(obj){
if(typeof obj != 'object' || obj == null){
return obj;
}
let result
if(obj instanceof Array){
result = []
}else{
result = {}
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
result[key] = deepClone(obj[key])
}
}
return result;
}
变量计算
1.字符串转换为数字可以用parseInt(),因为在有字符串 + …的式子,会强制把其他变量也转换为字符串
2.‘= =’:能强制转换之后相等的,会判断为true,如:100==‘100’,0==‘ ’,0 = =false ,null = = undifined;那么在什么时候使用= = 呢?一般只有这种情况:
if(obj.a == null) 来判断obj.a是否为null或者undifined。
3.if语句判断的是truely变量(!!a = = true)和false变量(!!a = = false),以下为所有的falsely变量:
!!0 === false;
!!NaN === false;
!!undefined === false;
!!null === false;
!!'' === false;
!!false === false;
判断arr是否是一个数组的方法:
1.Array.isArray(arr)
2.arr instanceof Array
原型和原型链
如何判断一个变量是不是数组?
手写一个简易的Jquery,考虑插件和扩展性
class的原型本质,怎么理解?
以一个class继承的代码为例
class People{
constructor(name){
this.name = name;
}
eat(){
console.log(`${this.name} eat rice`);
}
}
class Student extends People{
constructor(name,number){
super(name);
this.number = number;
}
sayHi(){
console.log(`我叫${this.name},我的学号是${this.number}`);
}
}
let xiaoming = new Student('小明', 12345);
instanceof:1. 判断new出来的实例对象是属于某一类(xiaoming instance of People,xiaoming instanceof Student, xiaoming instance of Object都为true )
2.[] instanceof Array 判断是否为数组
3.{} instanceof Object 判断是否为对象
4.[] instanceof Object 也会返回true
原型
每个class都有显式原型prototype;
每个实例都有隐式原型__proto__;
实例的__proto__指向对应class的prototype;
原型链
当xiaoming instanceof时,就相当于沿着xiaoming的__proto__去查找原型链对应的prototye,如果有那么该prototype对应的类就为true,否则为false.
作用域和闭包
this的不同应用场景,如何取值?
手写bind函数
实际开发中闭包的应用场景,举例说明
自由变量:当前作用域未定义,但使用了。那么就向上级作用域去查找,如果一直找到全局作用域都没找到,那就报错:xxx is not difined.
(闭包中)自由变量的查找
//函数作为返回值
function create(){
const a = 100;
return function fn(){
console.log(a);
}
}
const re = create();
const a = 200;
re();
// 函数作为传参
function print(fn){
const a = 200;
fn();
}
const a = 100;
function fn(){
console.log(a);
}
print(fn);
结论:自由变量的查找是在函数定义的地方向上级作用域查找,不是在执行的地方
闭包的影响
变量会常驻内存,得不到释放,所以闭包不要乱用,但是闭包并不一定会造成内存泄漏(因为内存泄漏是指垃圾变量没有被释放,而闭包中的变量不一定无用)
this
this取什么值,是在函数执行的时候确认,而不是在函数定义的时候确认。
箭头函数中的this,取它上级作用域的值,它自己没有this
以下是包含this的几种情况
```javascript
//情况一
function fn1(){
console.log(this);
}
fn1(); //window
fn1.call({x:100}); //{x:100}
fn2 = fn1.bind({x:200});
fn2();//{x:200}
//情况二
const zhangsan = {
name: '张三',
sayHi(){
console.log(this); //zhangsan这个对象
},
wait(){
setTimeout(function(){
console.log(this)//window
},1000)
}
}
//情况三
const lisi = {
name: '李四',
sayHi(){
console.log(this); //lisi这个对象
},
wait(){
setTimeout(() => {console.log(this)},1000)//lisi这个对象
}
}
this的使用场景及取值:
1.当做普通函数被调用——全局
2.使用call apply bind——指向传入的参数
3.作为对象方法调用——指向上级对象,函数属于谁就指向谁
4.在class的对象中调用——实例本身
5.箭头函数中——找上级作用域中this的值来确定
手写bind函数
Function.prototype.myBind = function(){
const args = Array.prototype.slice.call(arguments);
const a = args.shift();
const self = this;
return function(){
return self.call(a, args);
}
}
异步和单线程
同步和异步的区别是什么?
手写用promise加载一张图片
前端使用异步的场景
JS是单线程语言,只能同时做一件事儿
浏览器和nodejs支持JS启动进程,如web worker
JS和DOM渲染共用同一个线程,因为JS可以修改DOM结构
同步和异步的区别:
1.同步会阻塞代码执行,异步不会阻塞代码执行
前端使用异步的场景:
1.网络请求:如ajax图片加载
2.定时,如setTimeout
手写promise加载图片
const url1 = 'https://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png';
const url2 = 'https://pics5.baidu.com/feed/eac4b74543a98226dbbad777b05f51074b90eb4f.jpeg?token=27a371d8135063b0f0b46b46bfe244b6';
function loadImg(src){
return new Promise((resolve, reject) => {
const img = document.createElement('img');
img.onload = () =>{
resolve(img);
}
img.onerror = () =>{
const err = new Error(`图片加载失败${src}`);
reject(err)
}
img.src = src;
})
}
// loadImg(url1).then((img) =>
// {console.log(img.width)
// return img}).then((img) => {
// console.log(img.height)
// }).catch((err) => console.error(err))
loadImg(url1).then((img) =>
{console.log(img.width)
return img//返回一个普通对象
}).then((img) => {
console.log(img.height);
return loadImg(url2); //返回promise对象
}).then(img2 =>{
console.log(img2.width);
return img2;
}).then( img2 => {
console.log(img2.height);
}).catch((err) => console.error(err))
以下开始过渡到js的web api
DOM(Document Object Model)
DOM是哪种数据结构?
DOM操作常用API
attribute和property的区别
一次性插入多个DOM节点,考虑优化
DOM的本质:从html文件解析出来的树
获取DOM节点的方法:
1.document.getElementById( ) //元素
2.document.getElementsByTagName() //集合
3.document.getElementsByClassName() //集合
4.document.querySelectorAll()//集合
attribute和property的区别:
let plist = document.querySelectorAll('p');
let p = plist[0];
// propety是通过获取属性开改变页面样式的一种形式
p.style.width = '100px';
p.className = 'red';
console.log(p.style.width);
console.log(p.className);
console.log(p.nodeName);
console.log(p.nodeType);
//attribute
p.setAttribute('data-name', 'jiang');
console.log(p.getAttribute('data-name'))
p.setAttribute('style', 'font-size:50px')
1.property:修改对象属性,不会修改到对象属性中;
2.attribute:修改html属性,修改html结构;
3.两者都可能会引起DOM的重新渲染(所以建议使用property)
以上三点为看mooc视频时的总结,根据我自己的理解来讲,我认为这两者之间的区别还在于property只能修改DOM自带的属性,而attribute不仅可以修改dom自带的属性,还可以修改和创建一些自定义属性;
DOM结构操作:
const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');
//创建节点
const newP = document.createElement('p');
newP.innerHTML = 'this is newP';
//插入节点
div1.appendChild(newP);
//移动节点
const p1 = document.getElementById('p1');
div2.appendChild(p1);
//获取父元素
console.log(p1.parentNode);
//获取子元素
console.log(div1.childNodes);
const div1ChildsNodeP = Array.prototype.slice.call(div1.childNodes).filter(child =>{
if(child.nodeType === 1){
return true;
}else{
return false;
}
})
console.log(div1ChildsNodeP);//仅为标签的子元素
//删除子元素
div1.removeChild(div1ChildsNodeP[0]);
DOM性能:
1.缓存DOM查询结果
2.将频繁操作改成一次操作
const list = document.getElementById('list');
//创建一个文档片段,此时还没有插入DOM中,可以避免多次DOM操作
const frag = document.createDocumentFragment();
for(let i =0;i<10;i++){
const li = document.createElement('li');
li.innerHTML = `this is li${i}`;
frag.appendChild(li);//此处没有操作DOM
}
list.appendChild(frag);//此处操作了一次DOM
DOM的数据结构:树
BOM(Browser Object Model)
如何识别浏览器类型
分析拆解url的各个部分
//navigator
const ua = navigator.userAgent;//拿到浏览器的信息(包括类型)
const isChrome = ua.indexOf('Chrome');
console.log(isChrome)
//screen
console.log(screen.width);
console.log(screen.height);
//location
console.log(location.herf);//取网址
console.log(location.protocol); //取协议(http或https)
console.log(location.host)//取域名
console.log(location.search)//取参数
console.log(location.hash);//取hash
console.log(location.pathname)//取路径
//history
history.back();
history.forward();//网页前进或后退
事件绑定和事件冒泡
编写一个通用的事件监听函数
描述事件冒泡的流程
无限下拉的图片列表,如何监听每个图片的点击
通用的事件监听函数
function bindEvent(item, type,selector,fn){
if(fn == null){
fn = selector;
selector = null
}
item.addEventListener(type,event =>{
let targrt = event.target;
if(selector){//事件代理绑定
if(targrt.maches(selector)){
fn.call(target,event);
}
}else{//普通绑定
fn.call(target,event);
}
});
}
ajax
手写一个简易的ajax
跨域的常用实现方式
手写ajax
function ajax({body,headers,url,method}) {
return new Promise((resolve,reject) =>{
let request = new XMLHttpRequest();
request.open(method,url);
for(let key in headers){
let value = headers[key];
request.setRequestHeaders(key,value);
}
request.send(body);
request.onreadystatechange = () =>{
if(request.readyState === 4){
if(request.status === 200){
resolve(
JOSN.parse(request.responseText)
)
}else if(request.status === 400){
reject(new Error('request failed'))
}
}
}
})
}
xhr.readyState:
1.0-(未初始化)还没有调用send()方法;
2.1-(已调用send()方法)还在发送请求;
3.2-(载入完成)send()方法执行完成,已经接收到全部响应内容;
4.3-(交互)正在解析响应内容;
5.4-(完成)响应内容解析完成,可以在客户端调用
xhr.status(状态码):
2xx:表示成功处理请求,如200
3xx:需要重定向,浏览器重新跳转,301(永久重定向),302(临时重定向),304(资源未改变)
4xx:客户端请求错误,404(服务端找不到,请求错误),403(客户端没有权限)
5xx:服务端出错
同源策略:
ajax请求时,浏览器要求当前网页和server必须同源(安全)
同源:协议、域名、端口号三者都相同
img、link、script三个标签不受同源策略限制
img标签可用于统计打点,可使用第三方统计服务
link和script可使用CDN,CDN一般都是外域
script可以实现JSONP,后端也可以根据前端传来的拼接地址进行数据选择来传给前端
所有的跨域,都必须经过server端的允许和配合
cors–设置服务端http header也可以实现跨域:
待补充…
axios作为ajax插件,本质是对XMLHttpRequest进行了封装,它支持promise
存储
cookie localstorage sessionstorage的区别
cookie本身用于浏览器和server通讯,可以通过document.cookie修改,也用于本地存储。cookie的值是添加形式的,每次赋值只要没有对cookie中的同一变量就行重赋值,此次赋值不会覆盖之前的值,会直接添加在后面
cookie的特点:
存储大小,最大4KB
http请求时需要发送到服务端,增加请求数据量
只能用document.cookie = ‘…’,太过简陋
localstorage 和sessionstorage:
存储大小最大为5M
API简单易用,setItem ,getItem
不会随着http请求被发出去
localstorage数据会永久存储,除非代码手动删除,sessionstorage只存在于当前会话,浏览器关闭则清空。
开发环境
git
抓包
webpack babel
linux的常用命令
git常用命令(按实际项目中使用顺序来写)
git branch(检查当前状况下代码分支)
git checkout -d xxx (创建一个新的分支)
git status(检查当前的修改文件有哪些)
git show(检查修改内容)
git add . xxx(提交修改)
git checkout(撤销上一步修改)
git push origin xxx(推送到服务端)
git pull origin xxx (从服务端下载)
git merge(合并分支)
抓包
chales实现代理网站,手机连接电脑进行抓包,以及下载证书来获取https的信息
webpack和babel
使用原因:
1.ES6模块化,浏览器暂不支持
2.ES6语法,浏览器并不完全支持
3.压缩代码、整合代码,以让网页加载更快
webpack基础配置
1.仅执行打包(webpack.config.js)
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development', //production 开发/线上模式
entry: path.join(__dirname, 'src', 'index.js'), //找到当前目录下的src的index.js
output:{
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src', 'index.html'),
filename: 'index.html', //产出文件名,放在dist中
})
],
devServer:{
port: 3000,
contentBase: path.join(__dirname, 'dist')
}
}
2.将ES6转换为ES5
需要安装babel,配置如下
添加.babelrc文件
{
"presets": ["@babel/preset-env"]
}
在webpack.config.js中添加
module:{
rules:[
{
test: /\.js$/,
loader:['babel-loader'],
include: path.join(__dirname, 'src'),
exclude: /node_modules/
}
]
},
3.ES6模块化
如果需要导出多个模块,用export一次性导出,或者挨个导出,然后用解构赋值的方法来获取这些模块
如果只导出一个模块,那就要用export default 但是导出多个的时候如果用了default,就不能再用解构赋值获取了,只能单个导出之后,采用属性的方法来获取模块中的内容
export function fn(){
console.log(fn);
}
export const age = 10;
export const obj = {
name: 'zhangsan'
}
function fn(){
console.log(fn);
}
const age = 10;
const obj = {
name: 'lisi'
}
export {
fn,
age,
obj,
}
const xxx = {
nema: 'xxx'
}
export default xxx
4.webpack配置生产环境
需要修改的部分:
mode: 'production', //这里改了
entry: path.join(__dirname, 'src', 'index.js'), //找到当前目录下的src的index.js
output:{
filename: 'bundle.[contenthash].js',//这里改了
path: path.join(__dirname, 'dist')
},
有需要的话还可以配置一下package中的build命令
这里在bundle文件名后面加的cotenthash可以随机生成一个乱码,当文件被修改后,它就会改变,有利于性能优化和缓存,后面还会说到
Linux常用命令
ssh 用户名@IP地址 (登录线上机)
ls/li(查看文件夹)
clear (清屏)
mkdir xxx (创建文件夹)
rm -rf xxx (删除文件夹)
cd xxxxxx (进入某目录)
mv xxx yyy (把文件名xxx改为yyy)
mv xxx …/…/…/xxx (修改文件路径)
cp xxx xxx1(把xxx拷贝到xxx1)
touch xxx(新建文件)
vi/vim xxx(新建文件并打开,打开之后可以写入内容,i进入编辑模式,esc后退,:w保存, :q退出,:q!强制退出)
cat xxx(查看文件内容)
grep “关键字” xxx (在文件xxx中查找关键字)
运行环境
网页加载过程
性能优化
安全
从输入url到渲染出页面的整个过程
要加载html代码,媒体文件,如图片、视频等,javajscript css
1.DNS解析:域名 -> ip地址
2.浏览器根据IP地址向服务器发起http请求(包括TCP连接)
3.服务器处理http请求,并返回给浏览器
4.网页根据HTML代码生成DOM Tree,根据CSS代码生成CSSOM
5.将DOM Tree 和CSSOM 整合形成 Render Tree
6.浏览器根据Render Tree渲染页面
7.遇到script标签则暂停渲染,优先加载并执行JS代码,完成再继续
8.直至把 Render Tree渲染完成
为什么要把CSS放在head里,把JS放在body最后呢?
因为CSS里包含的样式最好是在DOM加载时就加载完,然后和DOM Tree一起渲染,否则每次改变样式都造成重新渲染,很麻烦
JS放在不放在最后,会因为JS导致渲染卡住
图片加载不会阻塞渲染
window.onload和DOMContentLoaded的区别:
window.onload页面的全部资源加载完才会执行,包括图片、视频
DOMContentLoaded在DOM渲染完即可执行,此时图片、视频可能还没有加载完
性能优化
1.原则
多使用内存、缓存等方法,减少CPU计算量,减少网络加载耗时(空间换时间)
2.从何入手
一、让加载更快(1)减少资源体积:压缩代码(如webpack打包压缩)
(2)减少访问次数:合并代码。SSR服务器渲染,缓存 (如CSS精灵图、webpack打包时给出口文件名加上contenthash)
(3)使用更快的网络:CDN
二、让渲染更快:(1)css放在head,JS放在body最下面
(2)尽早执行JS,用DOMContentLoaded触发
(3)懒加载(图片懒加载,上滑加载更多)
(4)对DOM查询进行缓存(见前面)
(5)频繁操作DOM,合并到一起插入DOM结构(见前面)
(6)节流throttle,防抖debounce
contenthash的原理:
静态资源加hash后缀,根据文件内容计算hash
文件内容不变,则hash不变,则url不变
Url和文件不变,则会自动触发http缓存机制,返回304
CDN:用来配置静态文件,CDN不变的话也会返回304
SSR:服务端渲染——将网页和数据一起加载,一起渲染
如果是非SSR(前后端分离)——先加载网页,再加载数据,再渲染数据
懒加载(待补充)
给图片的src设置为preview.png,然后将真正的图片地址赋值给一个自定义属性,当屏幕滑动到图片的位置时,再显示图片
防抖debounce
监听一个输入框,文字变化后触发change事件
直接用keyup事件,则会频繁触发chenge事件
防抖:用户输入结束或暂停时,才会触发change事件
手写防抖:
function debounce (fn, delay = 500){
let timer = null;
return function (){
if(timer){
clearTimeout(timer);
}
timer = setTimeout(() =>{
fn(this,arguments)
timer = null;
},delay)
}
}
input1.addEventListener('keyup', debounce(() => {
console.log(input1.value)
}),600)
节流throttle(用于比防抖更频繁的触发)
拖拽一个元素时,要随时拿到该元素被拖拽的 位置
直接用drag事件,则会频繁触发,很容易导致卡顿
节流:无论拖拽速度多快,都会每隔一定时间触发一次
function throttle(fn,delay){
let timer = null;
return function(){
if(timer){
return
}
timer = setTimeout(() =>{
fn.apply(this,arguments);
timer = null;
},delay)
}
}
安全
常见的web前端攻击方式有哪些?
XSS跨站脚本攻击
CSRF 跨站请求伪造
XSS跨站脚本攻击
如:一个网站,我在其中写了一个script标签,里面包含获取用户cookie的代码,当用户点入网站时,即可获取用户的cookie,发送到服务端
XSS预防:
1.替换特殊字符,如<变为< >变为>