前言
文章对大部分知识点做了总结,当然也不会面面俱到,有一些具体的实现步骤和底层代码并没有展示出来。 相关知识点的详解都有各自的参考链接,可以查缺补漏。
一、HTML
1.doctype 的作用是什么?
- DOCTYPE 是 html5 标准网页声明,且必须声明在HTML文档的第一行。来告知浏览器的解析器用什么文档标准解析这个文档,不同的渲染模式会影响到浏览器对于 CSS 代码甚至 JavaScript 脚本的解析。
2.HTML、XHTML、XML 有什么区别?
- HTML:HyperText Markup Language超文本标记语言,HTML是一种基本的WEB网页设计语言。
- XML主要用于存储数据和结构,JSON作用类似,但更加轻量高效,XML是一种跨平台的,与软、硬件无关的,处理与传输信息的工具。
- XHTML(可扩展超文本标记语言)是一个基于XML的标记语言。
3.常用的 meta 标签?
- charset,用于描述 HTML 文档的编码形式
<meta charset="UTF-8" >
- http-equiv,相当于http 的文件头作用,比如下面的代码就可以设置 http 的缓存过期日期
<meta http-equiv="expires" content="Wed, 20 Jun 2019 22:33:00 GMT">
- viewport,控制视口的大小和比例
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
4.script 标签中 defer 和 async 的区别?
- defer:script 被异步加载后并不会立刻执行,而是等待文档被解析完毕后执行。
- async:脚本加载完毕后立即执行。
5.前端储存的方式?
- cookies: 兼容性好,请求头自带 cookie 方便,缺点是大小只有4k,自动请求头加入 cookie 浪费流量,每个 domain 限制20个 cookie,使用起来麻烦需要自行封装。
- localStorage:HTML5 加入的以键值对(Key-Value)为标准的方式,优点是操作方便,永久性储存(除非手动删除),大小为5M,兼容IE8+。
- sessionStorage:与 localStorage 基本类似,区别是 sessionStorage 当页面关闭后会被清理,而且与 cookie、localStorage 不同,他不能在所有同源窗口中共享,是会话级别的储存方式。
- IndexedDB:NoSQL 数据库,用键值对进行储存,可以进行快速读取操作,非常适合 web 场景,同时用 JavaScript 进行操作会非常方便。
6.iframe有那些缺点?
- iframe会阻塞主页面的Onload事件,会影响页面的并行加载;
- 搜索引擎的检索程序无法解读这种页面,不利于SEO;
- 改进:通过javascript动态给iframe添加src属性值,这样可以绕开以上两个问题。
二、CSS
1.css盒模型
标准模型:宽高计算不包含 padding 和 border ;通过 box-sizing: content-box; 来设置(浏览器默认)。
IE模型:宽高计算包含 padding 和 border ;通过 box-sizing: border-box; 来设置。
2.BFC(块状格式化上下文)
特点:
-
是一个独立的容器,里面的元素和外面的元素互不影响;
-
BFC垂直方向的边距会发生重叠;
-
BFC 区域不会与浮动元素区域重叠;
-
计算 BFC 高度时,浮动元素也参与计算。
创建方式: -
float 值不为 none;
-
position 的值不为 static 或 relative;
-
display 为 inline-box, table, table-cell 等;
-
overflow 不为 visible
作用: -
清除浮动;
-
防止同一 BFC 容器中的相邻元素间的外边距重叠问题;
3.实现垂直居中布局
- translate
{
position:absolute;
top:50%;
left:50%;
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
- table
父级元素:{ display:table;}
子级元素: { display:table-cell;vertical-align:middle } - flex
父级元素:{ display:flex;flex-direction:row;justify-content:center;align-items:center;}
子级元素:{flex:1}
方法很多,不一一赘述
4.分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用场景。
- 结构上:display:none 会从渲染树中消失,元素不占据空间且无法点击;visibility: hidden 不会从渲染树中消失,元素继续占据空间但无法点击;opacity: 0 不会从渲染树消失,元素占据空间且可点击。
- 继承性:display: none 和 opacity: 0 是非继承属性;父元素设置了 display:none 或 opacity: 0,子元素无论怎么设置都无法显示;visibility: hidden 会被子元素继承,并且子元素可以通过设置设置 visibility: visible; 来取消隐藏。
- 性能:display: none 会引起重排,性能消耗较大;visibility: hidden 会引起重绘,性能消耗相对较小; opacity: 0 会重建图层,性能较高。
5.link 标签和 import 标签的区别
- link属于XHTML标签,link引用的css于页面同步加载。
- @import是css提供的一种方式,@import等到页面全部加载完加载,在CSS2.1以下不支持。
- link 方式样式的权重高于 @import 的;link 可以使用 js 动态引入,@import不行;
6.移动端 Retina 1px 像素问题的解决方案
- viewport + rem
- background-image
- 伪元素 + transform scale()
- box-shadow
7 种方法解决移动端 Retina 屏幕 1px 边框问题–推荐文章
7.文本显示行数控制
- 单行
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
- 多行
overflow: hidden;
text-overflow: ellipsis; // 超出显示'...'
display: -webkit-box; // 将元素作为弹性伸缩盒子模型显示 。
-webkit-line-clamp: 2; // 用来限制在一个块元素显示的文本的行数
-webkit-box-orient: vertical; // 设置或检索伸缩盒对象的子元素的排列方式
8. 清除浮动的方式
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
clear:both
clear:both
9.transition 和 animate 有何区别?
- transition:用于做过渡效果,没有帧概念,只有开始和结束状态,性能开销较小。
- animate:用于做动画,有帧的概念,可以重复触发且有中间状态,性能开销较大。
10.实现一个扇形
.sector {
width: 0;
height: 0;
border-width: 50px;
border-style: solid;
border-color: red transparent transparent;
border-radius: 50px;
}
三、JS
1.JS 的内置类型
- 基本类型:null、undefined、boolean、number、string、symbol
- 对象(Object):引用类型
tips:NaN也属于number类型,并且NaN不等于自身。
2.类型判断
- Typeof
tip:typeof对于基本类型,除了 null 都可以显示正确的类型;对于对象,除了函数都会显示 object
- Object.prototype.toString
3.原型和原型链的理解
- 原型:每个函数都有 prototype 属性,该属性指向原型对象;使用原型对象的好处是所有对象实例共享它所包含的属性和方法。
- 原型链:主要解决了继承的问题;每个对象都拥有一个原型对象,通过__proto__ 指针指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向 null。
下图展示了构造函数、原型对象、实例和原型链的关系:
4.闭包
- 闭包是指有权访问另一个函数作用域中的变量的函数。
- 闭包会使得函数内部的变量都被保存在内存中,造成较大的内存开销,因此不要滥用闭包。解决的方法是在退出函数之前将不使用的局部变量置为 null ;
经典面试题:改造下面的代码,使之输出0 - 9
for (var i = 0; i< 10; i++){
setTimeout(() => {
console.log(i);
}, 1000)
}
方法一、利用 setTimeout 函数的第三个参数,会作为回调函数的第一个参数传入
for (var i = 0; i < 10; i++) {
setTimeout(i => {
console.log(i);
}, 1000, i)
}
方法二、使用 let 变量 的特性
for (let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 1000)
}
等价于
for (let i = 0; i < 10; i++) {
let _i = i;// const _i = i;
setTimeout(() => {
console.log(_i);
}, 1000)
}
方法三、利用函数自执行的方式,把当前 for 循环过程中的 i 传递进去,构建出块级作用域。
for (var i = 0; i < 10; i++) {
(i => {
setTimeout(() => {
console.log(i);
}, 1000)
})(i)
}
6.this指向问题
this 的指向取决于函数以哪种方式调用:
- 作用函数调用:非严格模式下 this 指向全局对象,严格模式为 undefined;
- 作用方法调用:this 指向调用函数的对象;
- 构造函数调用:this 指向 new 创建出来的实例对象;
- call()和apply:它们的第一个参数为 this 的指向;
- 补充:箭头函数中的 this
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())
箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 this 是 window。并且 this 一旦绑定了上下文,就不会被任何代码改变。
7.call 和 apply 的实现
Function.prototype.call2 = function(context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args + ')');
delete context.fn
return result;
}
Function.prototype.apply = function (context, arr) {
var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {
result = context.fn();
}
else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('context.fn(' + args + ')')
}
delete context.fn
return result;
}
8.bind 的实现
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new TypeError("error");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
// 通过一个空函数作一个中转,避免绑定函数的 prototype 的属性被修改
var fNOP = function () {};
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
9.new 的实现原理
new 操作符做了什么?
- 创建一个空对象
- 然后让这个空对象的__proto__指向函数的原型prototype
- 执行构造函数中的代码,构造函数中的this指向该对象
- 如果构造函数有返回值,则以该对象作为返回值。若没有return或return了基本类型,则将新对象作为返回值
function objectFactory() {
var obj = new Object(),
Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
var ret = Constructor.apply(obj, arguments);
return typeof ret === 'object' ? ret : obj;
};
10.深浅拷贝
- 浅拷贝——如果被拷贝对象的元素是基本类型,就会拷贝出一份,并且互不影响。而如果被拷贝对象的元素是对象或者数组,就只会拷贝对象和数组的引用,此时若是在新旧对象上进行修改,都会相互影响。
// 数组浅拷贝:slice()、concat()
// 对象浅拷贝:Object.assign()、ES6的扩展运算符
- 深拷贝——完全的拷贝一个对象,即使嵌套了对象,两者也互相分离,修改对象的属性,也不会影响到另一个。
// 递归实现
function clone(source) {
var target = {};
for(var i in source) {
if (source.hasOwnProperty(i)) {
if (typeof source[i] === 'object') {
target[i] = clone(source[i]); // 如果是引用类型,则继续遍历
} else {
target[i] = source[i];
}
}
}
return target;
}
当然这只是简单的实现,没有考虑到特殊的情况,如对象或数组中的函数,正则等特殊类型的拷贝等。
// JSON.parse(JSON.stringify)
var arr = [
{ value: 1 },
{ value: 2 },
{ value: 3 }
];
var copyArr = JSON.parse(JSON.stringify(arr))
copyArr[0].value = 0;
console.log(arr); // [{value: 1}, { value: 2 }, { value: 3 }]
console.log(copyArr); // [{value: 0}, { value: 2 }, { value: 3 }]
上面这种方法简单粗暴,不能拷贝函数。
11.防抖和节流的实现(简易版)
- 防抖:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
funtion debounce(fn) {
// 创建一个标记用来存放定时器的返回值
let timeout = null;
return function() {
// 每次触发事件时都取消之前的延时调用方法
clearTimeout(timeout);
// 然后又创建一个新的 setTimeout, 这样就能保证 1000ms 间隔内如果重复触发就不会执行 fn 函数
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, 1000);
};
}
- 节流:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
function throttle(fn) {
// 通过闭包保存一个标记
let canRun = true;
return function(){
// 每次开始执行函数时都先判断标记是否为 true,不为 true 则 return
if (!canRun) return;
// 上一次定时器执行完后 canRun 为 true,所以要先设置为false
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
// 最后在 setTimeout 执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是 false,在开头被 return 掉
canRun = true;
}, 1000)
}
}
12.ES5继承的实现
// 组合继承
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
console.log(this.age);
}
13.JS 异步解决方案的发展历程以及优缺点
- 回调函数(callback)
ajax('XXX1', () => {
// callback 函数体
ajax('XXX2', () => {
// callback 函数体
ajax('XXX3', () => {
// callback 函数体
})
})
})
优点:解决了同步的问题,缺点:回调地狱,不能用 try catch 捕获错误,不能 return.
- Promise
ajax('XXX1')
.then(res => {
// 操作逻辑
return ajax('XXX2')
}).then(res => {
// 操作逻辑
return ajax('XXX3')
}).then(res => {
// 操作逻辑
})
优点:解决了回调地狱的问题,缺点:无法取消 Promise ,错误需要通过回调函数来捕获。
- Generator
function *fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
// 配合 co 库使用
const co = require('co')
function *fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
co(fetch()).then(data => {
//code
}).fetch(err => {
//code
})
优点:可以控制函数的执行,配合自动执行器 co 模块 简化了手动执行的步骤,缺点:不配合 co 函数库的话使用起来比较麻烦。
- async/await
// async其实是一个语法糖,它的实现就是将 Generator 函数和自动执行器(co),包装在一个函数中
async function test() {
// 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
// 如果有依赖性的话,其实就是解决回调地狱的例子了
await fetch('XXX1')
await fetch('XXX2')
await fetch('XXX3')
}
read().then((data) => {
//code
}).catch(err => {
//code
});
优点:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题,缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
14.Promise的简单实现
promise 的使用(有关 Promise 的详细用法,可参考阮一峰老师的ES6文档)
var promise = new Promise((resolve,reject) => {
if (操作成功) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function (value) {
// success
},function (value) {
// failure
})
简单实现
function myPromise(constructor) {
let self = this;
self.status = "pending" // 定义状态改变前的初始状态
self.value = undefined; // 定义状态为resolved的时候的状态
self.reason = undefined; // 定义状态为rejected的时候的状态
function resolve(value) {
if(self.status === "pending") {
self.value = value;
self.status = "resolved";
}
}
function reject(reason) {
if(self.status === "pending") {
self.reason = reason;
self.status = "rejected";
}
}
// 捕获构造异常
try {
constructor(resolve,reject);
} catch(e) {
reject(e);
}
}
添加 then 方法
myPromise.prototype.then = function(onFullfilled,onRejected) {
let self = this;
switch(self.status) {
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
var p = new myPromise(function(resolve,reject) {
resolve(1)
});
p.then(function(x) {
console.log(x) // 1
})
Promise实现原理这篇文章讲的很仔细,比较好理解。
四、网络基础
未完待更新
转载:https://blog.csdn.net/liulanghan111/article/details/100975979