参考博客:2020秋招前端面经知识点汇总(一)
1 HTML+CSS
1.1 基本标签
html文档标签:
<!DOCTYPE>
、<html>
、<head>
、<meta>
、<title>
、<link>
、<style>
、<body>
、<script>
、<noscript>
按闭合特征分类:
- 闭合标签:指由开始标签和结束标签组成的一对标签这种标签允许嵌套和承载内容,例如
<html></html>
、<p></p>
等。 - 空标签:指没有内容的标签,在开始标签中自动闭合。常见的空标签有:
<br>
、<hr>
、<img>
、<input>
、<link>
、<meta>
。
按是否换行特征分类:
- 块级元素:块级元素是值本身属性为
display:block
的元素。 - 内联元素:内联元素是指本身属性为
display:inline
的元素,其宽度随元素的内容而变化。
【补充】
display
属性
1、display 属性规定元素应该生成的框的类型
2、值:
none:此元素不会被显示
block:此元素将显示为块级元素,此元素前后会带有换行符
inline:默认。此元素会被显示为内联元素,元素前后没有换行符
inline-block:行内块元素
块级元素的特点:
- 每个块级元素独占一行,从上到下排布。
- 块级元素可以直接控制宽度高度等盒子模型相关的css属性。
- 不设置宽度的情况下,块级元素的宽度是其父级元素内容的宽度。
- 不设置高度的情况下,块级元素的高度是他本身内容的高度。
常用块级元素:
标签 | 描述 |
---|---|
div | 常用块级容器,也是css和layout的主要标签 |
h1-h6 | 一到六级标题 |
hr | 水平分割线 |
menu | 菜单列表 |
ol、ul、li | 有序列表、无序列表、列表项 |
dl、dt、dd | 定义描述列表、定义项目/名字、描述每一个项目/名字 |
table | 表格 |
p | 段落 |
form | 表单 |
内联元素的特点:
- 内联元素会和其他元素从左到右显示在一行中。
- 内联元素不能直接控制宽度高度以及盒子模型相关的css属性,可以设置内外边距的水平方向的值。也就是说,对于内联元素的margin和padding,只有margin-left/margin-right和padding-left/padding-right是有效的,竖直方向的margin和pading无效果。
- 内联元素的宽高是由其内容本身的大小决定的。
- 内联元素只能容纳文本或者其他内联元素,不允许在内联元素中嵌套块级元素。
常见的内联元素:
标签 | 描述 |
---|---|
a | 超链接( href 属性) |
b | 加粗 |
span | 常用的内联容器,定义文本块 |
img | 图片 |
input | 输入框 |
select | 创建单选或多选菜单,该元素是一种表单控件,可用于在表单中接受用户输入 |
strong | 加粗强调 |
label | 为 input 元素定义标注(标记) |
1.2 语义化标签
header
、section
、 footer
、aside
、nav
、 main
、article
、figure
、hgroup
、time
、mark
、address
例子:
<time datetime="2017-07-03">
<header>
<h1>信息科技有限公司</h1>
<nav>
<li><a href="#">首页</a></li>
<li><a href="aboutus_one.html">关于我们</a></li>
</nav>
</header>
1.3 HTML5新特性
1)语义化标签
2)表单input,如color、date、datetime、email等
3)视频和音频,如audio、video、source、embed、track等
4)canvas绘图
5)svg绘图
6)Geolocation(地理定位)用于定位用户的位置
7)拖放api
8)Web Worker
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。
参考博客:Web Worker 使用教程
9)Web Storage客户端存储数据
localStorage :始终有效,窗口或浏览器关闭也一直保存;所有同源窗口都是共享的
sessionStorage :针对一个 session 的数据存储, 当用户关闭浏览器窗口后,数据会被删除;不在不同的浏览器窗口共享,即使是同一个页面
cookie:只在设置的cookie过期时间之前一直有效,即使窗口和浏览器关闭;所有同源窗口都是共享的
常用API:
- 保存数据:localStorage.setItem(key,value);
- 读取数据:localStorage.getItem(key);
- 删除单个数据:localStorage.removeItem(key);
- 删除所有数据:localStorage.clear();
- 得到某个索引的key:localStorage.key(index);
10)WebSocket协议
HTTP协议只能发送单向请求,即只能客户端向服务端发送信息,然后服务端再返回给客户端,不能逆向进行;
而websocket可以实现,服务端主动向客户端推送信息,客户端也可以主动向服务端发送信息,是真正的双向信息交流。
协议标识符是ws(如果加密,则为wss)。
常用API:添加链接描述
参考博客:HTML5的十大新特性
1.4 布局
1.4.1 flex布局(弹性布局)
主轴、交叉轴、水平、垂直
flex容器的属性:
1)flex-direction:定义主轴的方向
2)flex-wrap:换行
3)flex-flow:是flex-direction属性和flex-wrap属性的简写形式
4)justify-content:定义flex项目在主轴上的对齐方式(左/右对齐,居中,两端对齐等)
5)align-items:定义flex项目在交叉轴上的对齐方式
6)align-content
flex项目的属性:
1)order
2)flex-grow
3)flex-shrink
4)flex-basis
5)flex
6)align-self:允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性
参考博客:Flex 布局教程:语法篇、Flex 布局教程:实例篇
1.4.2 盒模型
盒模型属性:
- margin:外边距
- padding:内边距
- border:边框
- content:内容
现在采用W3C盒模型作为标准的盒模型,即width指content部分的宽度。
1.4.3 两栏布局
基本方法同三栏布局。
1.4.4 三栏布局(圣杯布局/双飞翼布局)
左右固定,中间自适应。
1)网格布局(grid布局)
display:grid;
grid-template-rows: 100px;
/*auto关键字表示由浏览器自己决定长度*/
grid-template-columns: 300px auto 300px; /*第二列的宽度,基本上等于该列单元格的最大宽度*/
参考博客:CSS Grid 网格布局教程
fr关键字、auto-fill关键字、minmax()、repeat()、auto关键字
2)浮动
三栏固定高度,左右固定宽度,并分别设置左右浮动
.wrapper {
margin: 0 100px; /*关键点*/
}
.conter,
.left,
.right {
height: 100px;
}
.conter {
background-color: red;
width: 100%;
float: left; /*关键点*/
}
.left {
width: 100px;
background: green;
float: left; /*关键点*/
margin-left: -100px; /*关键点*/
}
.right {
width: 100px;
background: yellow;
float: right; /*关键点*/
margin-right: -100px; /*关键点*/
}
3)flex布局
容器设置display: flex;
,justify-content: space-between;
;
三栏固定高度,左右固定宽度;
中间设置flex:1;
;
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。
4)position定位
5)表格布局
1.5 水平居中
1)行内元素(内联元素)
-
判断其父元素是不是块级元素,如果是,则直接给父元素设置
text-align: center;
; -
如果不是,则将其父元素设置为块级元素,即
display: block;
,text-align: center;
2)块级元素
- 是否定宽
- 给出具体宽度的:需要居中的标签设置
margin: 0 auto;
- 没有给出具体宽度的:将需要居中显示的子元素转换成行内元素,即
display: inline-block;
或display: inline;
,再给父元素设置text-align: center;
- 给出具体宽度的:需要居中的标签设置
- 使用position定位属性:先设置父元素为相对定位(relative),再设置子元素为绝对定位(absolute),设置子元素的
left:50%
- 定宽度:设置绝对子元素的
margin-left: -元素宽度的一半px;
或者transform: translateX(-50%);
- 不定宽度:设置绝对子元素
transform: translateX(-50%);
- 定宽度:设置绝对子元素的
- flex布局实现:给待处理的块级元素的父元素添加属性
display: flex; justify-content: center;
1.6 垂直居中
1)单行的行内元素
- 设置单行行内元素的 “行高等于盒子的高” 即可
2)多行的行内元素
- 使用给父元素设置
display:table-cell;
和vertical-align: middle;
属即可
3)块级元素
- 使用position定位:先设置父元素为相对定位,再设置子元素为绝对定位,设置子元素的
top: 50%
,即让子元素的左上角垂直居中- 定高度:设置绝对子元素的
margin-top: -元素高度的一半px;
或者transform: translateY(-50%);
- 不定高度:设置绝对子元素
transform: translateY(-50%);
- 定高度:设置绝对子元素的
- 使用flex布局实现:给待处理的块状元素的父元素添加属性
display: flex; align-items: center;
1.7 水平垂直居中
1)已知高度和宽度的元素
- 设置父元素为相对定位,给子元素设置绝对定位,给需要水平垂直居中的子元素设置
top: 0; right: 0; bottom: 0; left: 0; margin: auto;
,或者该子元素设置left: 50%; top: 50%; margin-left: --该子元素宽度的一半px; margin-top: --该子元素高度的一半px;
2)未知高度和宽度的元素
- 设置父元素为相对定位,给子元素设置绝对定位,给需要水平垂直居中的子元素设置
left: 50%; top: 50%; transform: translateX(-50%) translateY(-50%);
- 设置父元素为flex定位,设置父元素
display: flex; justify-content: center; align-items: center;
参考博客:CSS水平居中+垂直居中+水平/垂直居中的方法总结
1.8 清除浮动的几种方式
为什么要清除浮动?
是为了解决父级元素因为子级浮动引起的内部高度为0的问题(父级变成一条线);
边框不能随内容显示;
margin、padding没有效果;
影响其他兄弟元素。
1.8.1 clear清除浮动
给谁清除浮动,就在其后面额外添加一个空白标签,对空白标签设置:clear:both;
,这对后续元素(块级或者行内)产生的影响都将消失。
1.8.2 父级添加overflow
给父级元素添加overflow:hidden;
。
1.8.3 使用after伪元素
给父元素添加伪类。
// 给父元素添加伪类,推荐此方法
.wrapper:after{
content: "";
clear:both;
display: block;
height: 0;
overflow: hidden;
visibility: hidden;
}
1.8.4 使用before和after双伪元素
/* wrapper为父元素 */
.wrapper:before,.wrapper:after{
content:"";
display:table;
}
.wrapper:after{
clear:both;
}
.wrapper{
*zoom:1;
/* zoom是IE专用属性,firefox等是不支持的。 */
/* 设置zoom:1可以在IE6下清除浮动、解决margin导致的重叠等问题。 */
}
参考博客:清除浮动的几种方法
1.9 三种常用的定位
固定定位flexd:
-
固定定位与绝对定位类似,但它是相对于浏览器窗口定位,并且不会随着滚动条进行滚动。
-
固定定位的最常见的一种用途是在页面中创建一个固定头部、固定脚部或者固定侧边栏,不需使用margin、border、padding。
相对定位relative:
- 相对于设置相对定位的该元素来进行定位,依据left、right、top、bottom等属性在正常文档流中偏移自身位置(以定位元素本身的位置为参考)
- 不会改变其他元素的布局,会在此元素本来位置留下空白
绝对定位absolute:
-
如果它的父元素设置了除static之外的定位,比如
position:relative
或position:absolute
及position:fixed;
,那么它就会相对于它的父元素来定位,位置通过left , top ,right ,bottom属性来规定;(以定位元素的父元素的位置为参考) -
如果它的父元素没有设置定位,那么就得看它父元素的父元素是否有设置定位,如果还是没有就继续向更高层的祖先元素类推,总之它的定位就是相对于设置了除static定位之外的定位的第一个祖先元素,如果所有的祖先元素都没有以上三种定位中的其中一种定位,那么它就会相对于文档body来定位(并非相对于浏览器窗口,相对于窗口定位的是fixed);
-
元素之间可层叠,会改变其他元素的布局,不会在此元素本来位置留下空白
默认定位static:
- 默认值,没有定位
粘性定位sticky与继承定位inherit
2 JavaScript
2.1 基本数据类型和引用数据类型
基本类型:Number
Boolean
String
Undefined
(未定义类型) Null
(存放在栈内存中)
引用类型:Object(Array, Date, RegExp, Function)
(存放在堆内存中)(除了5种基本数据类型之外,其他所有的都是Object对象)
参考博客: JS基本数据类型和引用数据类型的区别及深浅拷贝
2.2 类型转换
- 基本数据类型转换成数值类型
- 基本数据类型转换成字符串类型
- 基本数据类型转换成转换成布尔类型
// null转换为0; undefined转换为NaN(NaN其本质是一个number)
5 + null // 返回 5 因为 null 被转换为 0
// 空字符串转换成数值时,为0
// 字符串中既包含数字还包含字母,例如 ”abc123”,那么转换之后就是NaN
'abc123' // 返回NaN
// 通过 变量/常量 + ""/’’ 来转换为字符串,底层的本质其实就是调用String()函数
// 将null空类型通过String()的方式转换为字符串类型,转换之后还是null
// 将undefined未定义类型通过String()的方式转换为字符串类型,转换之后还是undefined
"5" + null // 返回 "5null" 因为 null 被转换为 "null"
"5" + 2 // 返回 52 因为 2 被转换为 "2"
// 通过+/-都将其它类型转换为数值类型
"5" - 2 // 返回 3 因为 "5" 被转换为 5
"5" * "2" // 返回 10 因为 "5" 和 "2" 被转换为 5 和 2
// 空字符串/0/NaN/undefined/null 转换成布尔类型时,会转换成false, 其它的都是true
==
:先转换类型,再比较
===
:全等于,不转换类型直接比较
参考博客: JavaScript中基本数据类型之间的转换
2.3 类型检查
typeof、instanceof、constructor、Object.prototype.toString.call()
// typeof对引用数据类型不起作用;对基本数据类型null输出的是object
console.log(typeof []); // object
console.log(typeof {
}); // object
console.log(typeof null); // object
console.log(typeof function(){
}); // function
// instanceof对引用数据类型有用,对于基本数据类型,需要先new对象
// 前三种不考虑null和undefined这两种特殊的基本数据类型
// Object.prototype.toString.call()能完全判断基本数据类型(包括null和undefined)和引用数据类型
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call([])); // [object Array]
参考博客:js检测数据类型四种方法
应用: 判断变量为数组的方法
1)instanceof
var arr=[];
console.log(arr instanceof Array); //返回true
2)constructor
console.log([].constructor == Array); // 返回true
3)Object.prototype.toString.call(arr) === “[object Array]”
function isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]';
}
console.log(isArray([])); // 返回true
// 或者直接这样:
console.log(Object.prototype.toString.call([]) === '[object Array]') // 返回true
4)Array.isArray()
console.log(Array.isArray([]) )// 返回true
2.4 数组
2.4.1 常用API接口
-
push():向数组末尾添加一个或者多个元素,并返回新的长度
-
pop():删除数组的最后一个元素,把数组的长度减1,并且返回它被删除元素的值,如果数组变为空,则该方法不改变数组,返回undefine值
-
unshift():是向数组的开头添加一个或多个元素,并且返回新的长度
-
shift():用于把数组的第一个元素从其中删除,并返回被删除的值。如果数组是空的,- shift()方法将不进行任何操作,返回undefined的值
-
join() :数组转字符串,方法只接收一个参数:即默认为逗号分隔符
-
reverse() (反转数组):方法用于颠倒数组中元素的顺序
-
concat() :用于连接两个或多个数组
-
slice():arr.slice(start , end); 数组截取,返回一个新的数组,包含从 start 到 end (不包括该元素)的 arr 中的元素
-
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目
-
indexOf() :返回某个指定的字符串值在字符串中首次出现的位置
-
split() :用于把一个字符串分割成字符串数组
-
includes() :用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。
模拟栈方法和队列方法:
-
栈方法 ------ 提供push()和pop()方法,实现类似栈的行为
push()向数组末尾添加项,返回修改后数组长度。pop()从数组末尾移除最后一项,减少数组长度,返回移除的项。
-
队列方法 ------ 提供push()和shift()方法,实现类似队列的行为
shift()移除数组中的第一个项并返回该项,同时将数组长度减1。
2.4.2 排序
默认地,sort() 函数根据字符串Unicode码进行排序。
比较函数实现数值排序:nums.sort((a,b) = > a-b); // 升序
2.4.3 遍历
1)for循环
2)for in(不仅可以遍历数组,还可以遍历对象)
3)for of(常用遍历数组的方式,ES6新增)
let arr=[1,2,3];
for (let key of arr){
console.log(key);
}
// 输出:1 2 3
4)forEach(遍历数组,不能break 或者return)
arr = [1,2,3];
arr.forEach((data,index)=>{
console.log(data,index);
})
// 输出如下:
// 1 0
// 2 1
// 3 2
5)map
执行callback函数返回的结果组成一个新数组,不改变原数组。
但,未被初始化项、已经被删除项(使用delete等方法)、数组的属性不会被执行callback函数。
let arr=new Array(5);
arr[1]=1;
arr[2]=2;
arr.name="hello,world!"; // name为数组的属性
let fireArr=arr.map(item=>item*5);
console.log(fireArr); // 输出:[empty,5,10,empty,empty],也可以这样表示:Array(5) [ <1 empty slot>, 5, 10, <2 empty slots> ]
6)reduce
让数组的前项后项进行某种计算,并返回累计计算的结果。
结构:
arr.reduce(function(prev,cur,index,arr){
...
}, init);
arr 表示原数组;
prev 表示上一次调用回调时的返回值,或者初始值 init;
cur 表示当前正在处理的数组元素;
index 表示当前正在处理的数组元素的索引,若提供 init 值,则索引为0,否则索引为1;
init 表示初始值。
arr = [1,2,3,3];
// 求数组之和
var sum = arr.reduce((prev, cur) => {
return prev + cur;
},0);
console.log(sum); // 打印9
// 求数组最大值
var max = arr.reduce(function (prev, cur) {
return Math.max(prev,cur);
});
7)every
执行callback函数的返回值一旦为 false 就立刻返回 false 。
否则,callback为每个元素返回true,every就返回true。(如果每一项返回true,则返回true)
arr = [1,2,3,4,5];
let result = arr.every(function (item, index) {
return item > 0
});
console.log(result); // 打印true
8)some
执行callback函数的返回值一旦为 true 就立刻返回 true 。
9)filter
筛选符合某种条件的元素,将符合条件的元素重新组成一个新的数组。
arr = [2,3,4,5,6];
arr.name=8; // 数组的属性
let morearr = arr.filter((item) => {
return item > 3
});
console.log(morearr); // 打印[ 4, 5, 6 ]
参考博客:JavaScript中数组中遍历的方法
2.4.4 伪数组(类数组对象)
arguments对象:
arguments对象是所有(非箭头)函数中都可用的局部变量,它引用着函数的实参,可以用数组下标的方式"[]"引用arguments的元素。
当实参的个数比形参的个数多的时候,则可以通过arguments来访问实参。
arguments对象不是一个 Array(而是伪数组),除了length属性和索引元素之外没有任何Array属性。
arguments.length为函数实参个数,arguments.callee引用函数自身。
比如 func(‘a’, ‘b’, ‘c’) 的时候,func里面获得的arguments[0] 是 ‘a’,arguments[1] 是 ‘b’,依次类推。但问题在于这个arguments对象其实并不是Array,所以没有slice方法。Array.prototype.slice.call( )可以间接对其实现slice的效果,而且返回的结果是真正的Array。
但是它可以被转换为一个真正的Array:
// 方法一:
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);
// 方法二:
const args = Array.from(arguments);
// 方法三:
const args = [...arguments];
伪数组:
伪数组就是类似数组的对象,含有length属性,例如:
obj={
"0":"zhang",
"1":18,
length:2
}
// 这个就是伪数组,含有length属性
与数组的异同:
- 对象没有数组Array.prototype的属性值,伪数组类型是Object,而数组类型是Array
- 伪数组是键值对,数组是索引,其length会自动更新
- 伪数组不具有数组所拥有的方法
- 伪数组存在的意义,是可以让普通的对象也能正常使用数组的很多算法
主要有以下两类:
- 典型的是函数的 argument参数
- 像调用getElementsByTagName,document.childNodes之类的,它们都返回 NodeList对象,都属于伪数组
- jQuery 对象,比如 $(“div”)
2.4.5 数组去重
利用set对象。
Array.from()函数:
-
用来将其他对象转换成数组
- 部署了Iterator接口的对象,比如:Set,Map,Array
- 类数组对象,就是一个对象必须有length属性,没有length,转出来的就是空数组
-
Array.from(arrayLike[, mapFn[, thisArg]])
- arrayLike:被转换的的对象。
- mapFn:map函数。
- thisArg:map函数中this指向的对象。
js属性对象的hasOwnProperty方法:
- 判断对象是否包含特定的自身(非继承)属性,该方法返回一个布尔值
参考博客:JavaScript数组去重(12种方法,史上最全)
2.4.6 数组乱序
方法:sort和Math.random()结合、Fisher-Yates
参考博客:javaScript如何实现真正的数组乱序?、「前端进阶」数组乱序
2.5 字符串
1)length
2)str[]、charAt()、charCodeAt()
3)indexOf()、lastIndexOf()
4)search(),一般与正则表达式配合使用
5)slice()、substring()、substr()
6)replace()、concat()
7)toUpperCase()、toLowerCase()
8)trim()删除字符串两端的空白符
9)split()将字符串转换成数组,可接受正则匹配
2.6 闭包
作用域(全局变量和局部变量)
任务队列
执行上下文:
执行上下文包括 函数执行上下文 和 全局执行上下文。
子对象会一级一级地向上寻找所有父对象的变量。
匿名函数(没有函数名的函数)的两种定义方式:
var double = function(x){return x+x;}
(function(x,y){alert(x+y)})(2,3)
第一个括号创建匿名函数,第二个括号传入参数调用该函数
匿名函数的一大用处,便是用来创建闭包。
函数内部的变量声明,若没有使用关键字var,则该变量被当做全局变量使用。
函数内部的变量可以读取全局变量;函数外部的变量不能读取函数内的局部变量,怎么解决这个呢,那就使用闭包。
闭包:
- 能读取其他函数内部变量的函数,也可以说是定义在函数内部的函数(子函数)。
function f1(){
var n=999;
function f2(){
// f2就是闭包,将其作为返回值赋值给result
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
闭包的用途:
- 读取函数内部的变量
- 让这些变量的值始终保存在内存中
闭包的缺点:
- 内存消耗太大,影响浏览器性能
- 造成内存泄露
常见面试题:
- for 循环中打印
function f(){
for(var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i++);
}, 4000);
}
console.log("for循环外:", i);
}
f();
// 输出结果:
// for循环外: 5
// 5
// 6
// 7
// 8
// 9
// 解决方法:
function f(){
for(var i = 0; i < 5; i++){
(function(x){
// 立即执行函数
setTimeout(function(){
console.log(x++); // 4次for循环计算的i值(0、1、2、3、4)存放在任务队列中
}, 4000);
})(i);
}
console.log("for循环外:", i); // 先执行 执行栈stack 中的任务,间隔4秒,再执行 任务队列 中的任务
}
f();
// 输出结果:
// for循环外: 5
// 0
// 1
// 2
// 3
// 4
闭包允许内层函数引用父函数中的变量,但是该变量是最终值。
视频讲解:JavaScript闭包 - Web前端工程师面试题讲解
- 真实的获取多个元素并添加点击事件
var op = document.querySelectorAll("p");
for (var j = 0; j < op.length; j++) {
op[j].onclick = function() {
alert(j); // alert出来的值是一样的
};
}
// 解决办法:
for (var j = 0; j < op.length; j++) {
(function(j) {
op[j].onclick = function() {
alert(j);
};
})(j);
}
参考博客:学习Javascript闭包、JavaScript中的匿名函数及函数的闭包
2.7 DOM
1)节点层级
- node类型
- 属性:nodeType、nodeName、nodeValue、childNodes(NodeList)
- 方法:appendChild()、removeChild()、replaceChild()、insertBefore()
- document类型
- document.documentElement 指向 html 元素,document.body 指向 body 元素
- document.title、document.URL、document.forms等
- 定位元素:getElementById()、getElementsByTagName()、getElementsByName()
- 文档写入:document.write()、document.open()、document.close()等
- element类型
- HTML元素
- 属性:id、classname(即html中的class属性)、lang、dir、title
- 方法:getAttribute()、setAttribute()、removeAttribute()、createElement()
- 先创建document.createElement()新元素,再document.body.appendChild()将其添加到body元素中
- HTML元素
- text类型
- comment类型
- CDATASection类型
- DocumentFragment类型
- Attr类型
2)MutationObserver接口
observer()方法
3)Selectors API
2.8 事件
事件处理、事件冒泡、事件捕获、事件委托、浏览器事件机制(DOM事件流,即捕获、目标、冒泡)
事件绑定方式(也叫做事件监听方式,如onclick类型的传统事件绑定方式、HTML触发、addEventListener())
事件循环与消息队列
参考博客:JavaScript事件处理、JavaScript事件处理机制、原生js——事件监听方法、JavaScript 事件循环机制、
JS事件模型
2.9 变量提升和函数提升(预编译)
函数提升和函数表达式(分为匿名函数表达式和具名函数表达式)
JavaScript中的函数是一等公民,函数声明的优先级最高,它永远被提升至作用域最顶部,然后才是函数表达式和变量按顺序执行。
例子:
var foo = 3;
function hoistFunction() {
console.log(foo); // 此处会打印函数 function foo() {}
foo = 5;
console.log(foo); // 5
function foo() {
}
}
hoistFunction();
console.log(foo); // 3
预编译之后:
// 预编译之后
var foo = 3;
function hoistFunction() {
var foo;
foo = function foo() {
};
console.log(foo); // function foo() {}
foo = 5;
console.log(foo); // 5
}
hoistFunction();
console.log(foo); // 3
2.10 原型链
-
实例就是对象,实例的__protpo__指向的是原型对象。
-
实例的构造函数的prototype也是指向的原型对象。
-
原型对象的construor指向的是构造函数。
举例,
var M = function (name) {
this.name = name; }
var o3 = new M('o3')
原型链:
-
每个对象(实例)都可以有一个原型对象,这个原型还可以有它自己的原型,以此类推,形成一个原型链。
-
查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找…
应用:
var M = function (name) {
this.name = name; } // 构造函数
var o3 = new M('o3') // 对象(实例)
var o5 = new M()
o3.__proto__.say=furnction(){
// o3.__proto__指向原型对象
console.log('hello world')
}
o3.say() // 打印hello world
o5.say() // 打印hello world
-
通过一个构造函数创建出来的多个实例,如果都要添加一个方法,给每个实例去添加并不是一个明智的选择,这时就该用上原型了。
-
在实例的原型对象上添加一个方法,这个原型的所有实例便都有了这个方法。
注意:
-
只有函数有prototype,对象是没有的。
-
但是函数也是有__proto__的,因为函数也是对象。函数的__proto__指向的是Function.prototype。也就是说普通函数是Function这个构造函数的一个实例。
-
instanceof是判断实例对象的__proto__和生成该实例的构造函数的prototype是不是引用的同一个地址,是返回true,否返回false。
-
判断实例是由哪个构造函数生成的,需要用到constructor。
-
所有JavaScript的内置构造函数都是继承自 Object.prototype。
2.11 继承
先定义一个父类,
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
// Animal.prototype指向原型对象
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
js实现继承的几种方式如下,
1)原型链继承
将父类的实例作为子类的原型
function Cat(){
}
Cat.prototype = new Animal(); // 将父类的实例作为子类的原型
Cat.prototype.name = 'cat';
// Test Code
var cat = new Cat();
console.log(cat.name); // cat
console.log(cat.eat('fish')); // cat正在吃:fish
console.log(cat.sleep()); // cat正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
2)构造继承
使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)(复制父类的实例属性给子类)
3)实例继承(原型式继承)
为父类实例添加新特性,作为子类实例返回
4)拷贝继承
5)组合继承
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
6)继承组合继承
通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点
参考博客:JS实现继承的几种方式
2.12 内存泄露
不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。
常见的内存泄露:
- 意外的全局变量:一个未声明变量的引用会在全局对象中创建一个新的变量。
- 被遗忘的定时器和回调函数
- DOM 之外的引用
- 闭包
参考博客:添加链接描述、JavaScript中的垃圾回收和内存泄漏
2.13 垃圾回收机制
找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是时时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。
常用方法:
- 标记清除(垃圾收集器)
- 引用计数
2.14 防抖与节流
防抖:
- 将若干个函数调用合成为一次,并在给定时间过去之后仅被调用一次
节流:
- 节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数
参考博客:JS简单实现防抖和节流
2.15 深拷贝与浅拷贝
深复制和浅复制最根本的区别在于是否是真正获取了一个对象的复制实体,而不是引用。
- 浅复制 ----- 只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用。换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。
- 深复制 ----- 在计算机中开辟了一块新的内存地址用于存放复制的对象。如果源对象发生了改变,复制的对象并不会发生变化。
通俗一点理解就是浅拷贝出来的数据并不独立,如果被复制的对象改变了,那么浅拷贝的对象也会改变,深拷贝之后就会完全独立,与拷贝对象断绝关系。
2.16 ajax, fetch, axios
参考博客:从ajax到fetch、axios 、一文秒懂 ajax, fetch, axios、Jquery ajax, Axios, Fetch区别之我见
2.17 js跨域
2.18 new实例化的实现过程
- 创建一个空对象Var obj={};
- 设置新对象的constructor属性为构造函数的名称,将新对象的__proto__指向构造函数的prototype,Obj.proto==ClassA.prototype
- 使用新对象调用构造函数,将构造函数中this指向新实例对象,ClassA.call(obj)
- 将初始化完毕的新对象地址,保存到等号左边的变量中
- 若构造函数中返回this或返回值是基本类型(number,string,bool,null,undefined)或者无返回值,则返回新的实例对象,若是引用类型的值,则返回这个引用类型。
参考博客:javascript中new关键字详解