前端知识之HTML和JavaScript
html
html标签的类型(head、body、!DOCTYPE)它们的作用是什么?
!DOCTYPE
- 它是指示web浏览器关于页面使用哪个HTML版本进行编写的指令
head
- 是所有头部元素的容器,绝大多数头部标签的内容不会显示给读者
- 该标签下所包含的部分可加入的标签有,
<base>
,<link>
,<script>
,<meta>
,<title>
以及<style>
body
- 用于定义文档的主体,包含了文档的所有内容
- 该标签支持html的全局属性和事件属性
h5新特性
- 新增选择器
document.querySelector
document.querySelectorAll
- 拖拽释放API
- Drag and drop
- 媒体播放的
video
的audio
- 本地储存
localStorage
sessionStorage
- 离线应用
- manifest
- 桌面通知
- Notifications
- 语义化标签
article
footer
header
nav
section
- 增强表单控件
calendar
date
time
email
url
search
- 地理位置
Geolocation
- 多任务
web worker
- 全双工通讯协议
websocket
- 历史管理
history
- 跨域资源共享
CORS(Access-Control-Allow-Origin)
- 页面可见性改变事件
visibilitychange
- 跨窗口通讯
PostMessage
Form Data
对象- 绘画
canvas
H5移除的元素:
- 纯表现的元素
basefont
big
center
font
s
strike
tt
u
- 对可用性产生负面影响的元素
frame
frameset
noframes
伪类和伪元素
伪类:用于已有元素处于某种状态时为其台南佳对应的样式,这个状态是根据用户的行为而动态变化的
:hover
:active
:focus
:link
:visited
:first-child
:lang()
:last-child
:nth-child()
- ...
伪元素:用于创建一些不在DOM树中的元素,并为其添加样式
::before
::after
::first-letter
::first-line
::selection
HTML5语义化
在HTML5出现之前我们最喜欢使用 <div>
来表示一切,其本身并没有什么具体含义,也不利于SEO。HTML5添加了一些语义化标签,用于更好的DOM结构。
<hn>
- h1~h6 分级标题
<ul>
<ol>
<li>
- 有序 / 无序列表
<header>
- 页眉,包含网站标示、导航、链接、搜索等
<main>
- 页面主要内容,一个页面只能用一次。
<footer>
- 页脚
<article>
- 表示文档、页面、应用或网站中的独立结构,其意在成为可独立分配的或可复用的结构,如在发布中,它可能是论坛帖子、杂志或新闻文章、博客、用户提交的评论、交互式组件,或者其他独立的内容项目。
<section>
- 表示 HTML 文档中一个通用独立章节,它没有更具体的语义元素来表示。一般来说会包含一个标题。
<aside>
- 定义所处内容以外的内容
<nav>
- 标记导航
文字相关的,标签同时含有文字样式以及更好的语义化:
<small></small> <!-- HTML 中的<small>元素將使文本的字体变小一号。在 HTML5 中,除了它的样式含义,这个元素被重新定义为表示边注释和附属细则,包括版权和法律文本。 -->
<strong></strong> <!-- 表示文本十分重要,一般用粗体显示。 -->
<em></em> <!-- HTML 着重元素 (<em>) 标记出需要用户着重阅读的内容, <em> 元素是可以嵌套的,嵌套层次越深,则其包含的内容被认定为越需要着重阅读。
通常地,该元素会被浏览器展示为斜体文本, 但是,它不应该仅仅用于应用斜体样式;为此目的,请使用 CSS 样式
使用 <cite> 元素标记作品的标题(书籍,戏剧,歌曲等);它通常也采用斜体样式,但具有不同的含义。使用 <strong> 元素标记比周围文本更重要的文本。-->
<mark></mark> <!-- 表示为引用或符号目的而标记或突出显示的文本,这是由于标记的段落在封闭上下文中的相关性或重要性造成的。 -->
<figure></figure> <!-- 代表一段独立的内容,并且作为一个独立的引用单元。当它属于主内容流(main flow)时,它的位置独立于主体。这个标签经常是在主文中引用的图片,插图,表格,代码段等等, -->
<figcaption></figcaption> <!--用于描述其父节点 <figure> 元素里的其他数据。<figcaption> 是<figure> 子标签第一个或最后一个。 -->
<cite></cite> <!-- 引用标签,表示一个作品的引用,且必须包含作品的标题。这个引用可能是一个根据适当的上下文约定关联引用的元数据的缩写。 -->
<blockquote></blockquote> <!-- 块级引用元素,代表其中的文字是引用内容。通常在渲染时,这部分的内容会有一定的缩进 -->
<q></q> <!-- 引用标签 (<q>) 表示一个封闭的并且是短的行内引用的文本。这个标签是用来引用短的文本,所以请不要引入换行符; 对于长的文本的引用请使用 <blockquote> 替代。 -->
<time></time> <!-- HTML time 标签用来表示 24 小时制时间或者公历日期,若表示日期则也可包含时间和时区。 -->
<abbr title=""></abbr> <!-- HTML 缩写元素(<abbr>)用于代表缩写,并且可以通过可选的 title 属性提供完整的描述。若使用 title 属性,则它必须且仅可包含完整的描述内容。 -->
<dfn></dfn> <!-- HTML 定义元素表示术语的一个定义。 -->
<address></address> <!-- 表示其中的 HTML 提供了某个人或某个组织(等等)的联系信息。 -->
<del></del> <!-- 表示一些被从文档中删除的文字内容。比如可以在需要显示修改记录或者源代码差异的情况使用这个标签。 -->
<ins></ins> <!-- 表示文档中添加的内容。 -->
<code></code> <!-- 呈现一段计算机代码。默认情况下,它以浏览器的默认等宽字体显示。 -->
<pre></pre> <!-- 表示预定义格式文本。在该元素中的文本通常按照原文件中的编排,以等宽字体的形式展现出来,文本中的空白符(比如空格和换行符)都会显示出来。 -->
<meter></meter> <!-- 用来显示已知范围的标量值或者分数值。 -->
<progress></progress> <!-- 用来显示一项任务的完成进度。 -->
推荐文章:
语义化的优点:
- 易于用户阅读,样式丢失的时候能让页面呈现清晰的结构
- 有利于SEO,搜索引擎根据标签来确定上下文和各个关键字的权重
- 方便其它设备解析
- 有利于开发和维护,语义化是代码更加具有可读性
audio标签的API
audio 常用属性:
src
音乐的urlpreload
预加载(autoplay开启的话,则失效)loop
循环播放controls
控制条autoplay
自动播放
js可获取到的属性:
duration
时长paused
是否暂停ended
是否播放完毕muted
是否静音volume
音量大小,0~1startTime
起始播放时间error
错误代码;1是用户终止;2是网络错误;3是解析错误;4URL无效currentTime
当前播放的时间currentSrc
当前的audio文件
audio控制函数:
load()
加载文件play()
加载并播放音频pause()
暂停canPlayType(obj)
测试是否支持给定的mini类型的文件
audio事件:
loadstart
开始请求数据progress
正在缓冲play
play() 或 autoplay 触发时pause
pause() 触发时ended
播放结束timeupdate
播放时间发生改变时canplaythrouph
歌曲已经加载完成canplay
缓冲至目前可播放状态
JavaScript基础
var/let/const
ES6新增? | 区别 | |
---|---|---|
var | x | 变量未赋值时未undefined ;为方法作用域,只要在方法内定义了,整个方法 定义变量后的代码都可以使用 |
let | √ | 变量声明前使用会报错,而var不会报错,只会显示undefined ; 是块作用域; let禁止重复声明变量,否则会报错,而var可以 |
const | √ | 常量声明方式,且必须在声明变量时初始化,后面也不能再改; 实际上只是保证变量指向的那个地址不得改动 |
js数据类型
基本数据类型 | 引用数据类型 | |
---|---|---|
包含 | Number ,String ,Booolean ,null ,undefined ,Symbol ,bigint (为ES6新增) | object 、function 。其中function 比较特殊。 |
储存 | 直接储存在栈中的简单数据段,占用空间小,大小固定,属于被频繁使用的数据。 | 引用数据类型是储存在堆内存中,占据空间大,大小不固定。引用数据类型在栈中储存了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获取实体 |
区别 | 堆比栈空间大,栈比堆运行速度快; 堆内存是无序储存,可以根据引用直接获取。 |
注: 栈是储存基本类型值和执行代码的空间。 ES6泛指2015年以后的所有新增特性
Object.assign
作用: Object.assign 可以实现对象的合并。
语法: Object.assign(target, ...sources)
解析:
Object.assign
会将source
里面的可枚举属性复制到target
,如果和target
中已有的属性将会覆盖- 后续的
source
会覆盖前面的source
的同名属性 Object.assign
复制的是属性值,如果属性值是一个引用类型,那么复制的其实是引用地址,就会存在引用共享的问题。
constructor
创建的每个函数都有一个prototype
(原型)对象,这个属性是一个指针,指向一个对象。在默认情况下,所有原型对象都会自动获得一个constructor
(构造函数)的属性,这个属性是一个指向prototype
属性所在函数的指针。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(继承自构造函数的prototype
),指向构造函数的原型对象。注意当将构造函数的prototype
设置等于一个以对象字面量形式创建的新对象时,constructor
属性不在指向该构造函数。
map 和 forEach 的区别
相同点:
- 都是循环遍历数组
- 每次执行匿名函数都支持三个参数:item当前每一项,index索引值,arr原数组
- 匿名函数中的this都是指向window
- 只能遍历数组
不同点:
map()
会分配内存空间储存新数组并返回,forEach()
不会返回数据。forEach()
允许callback
更改原始数组的元素,map()
返回新的数组并不允许改变原数组。
for of 可以遍历哪些对象
for...of...
是 es6
新增的遍历方法,但只限于迭代器(iterator
),所以普通的对象用 for...of
遍历会报错。
可迭代的对象有:包括Array
Map
Set
TypedArray
arguments
web component
web component
总的来说是提供一整套完善的封装机制来把Web组件化进行标准化;每个框架实现的组件都同意标准的进行输入输出,更好的进行复用。
web component
包含四个部分:
custom element
- 提供一种方法让开发者可以自定义HTML元素,包含特定的组合,样式和行为。支持
web component
标准的浏览器会提供一些列API给开发者用于创建自定义的元素,或者扩展现有元素
- 提供一种方法让开发者可以自定义HTML元素,包含特定的组合,样式和行为。支持
html import
- 一种早在HTMLs中引用以及复用其它HTML文档的方式。
html template
- 模板
shadow DOM
- 提供一种更好的组织页面元素的方式,避免代码间相互影响。
变量提升
Javascript是单线程语言(动态编译),执行是按顺序执行的,但是并不是逐行的分析和执行,而是一段一段的分析执行,会先进行编译阶段才是执行阶段。在编译阶段,代码真正执行前的几毫秒,会检测到所有的变量和函数声明,所有这些函数和变量声明都被添加到名为Lexical Environment
的JavaScript
数据结构的内存中,所以这些变量和函数能在它们真正被声明之前使用。
作用域
作用域是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同的作用域下同名变量不会有冲突。
ES6之前, JavaScript没有块级作用域,只有全局作用域和函数作用域。
Objects 和 maps 的比较
Object 和 Map 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Map 使用。
不过 Map 和 Object 有一些重要的区别,在下列情况中使用 Map 会是更好的选择:
Map | Object | |
---|---|---|
意外的键 | Map 默认情况不包含任何键,只包含显式插入的键。 | 一个 Object 有一个原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。(备注:虽然从 ES5 开始可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。) |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。 | 一个 Object 的键必须是一个 String 或是 Symbol |
键的顺序 | Map 中的键是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。 | 虽然 Object 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的。因此,最好不要依赖属性的顺序。[1] |
Size | Map 的键值对个数可以轻易地通过 size 属性获取。 | Object 的键值对个数只能手动计算. |
迭代 | Map 是 可迭代的 的 | Object 没有实现 迭代协议,所以使用 JavaSctipt 的 for...of 表达式并不能直接迭代对象。[2] |
性能 | 在频繁增删键值对的场景下表现更好。 | 在频繁添加和删除键值对的场景下未作出优化。 |
序列化和解析 | 没有元素的序列化和解析的支持。解决方案 - stackoverflow | 原生的由 Object 到 JSON 的序列化支持,使用 JSON.stringify() 。原生的由 JSON 到 Object 的解析支持,使用 JSON.parse() 。 |
注:
- 自 ECMAScript 2015 规范以来,对象的属性被定义为是有序的;ECMAScript 2020 则额外定义了继承属性的顺序。但是,请注意没有可以迭代对象所有属性的机制,每一种机制只包含了属性的不同子集。
for-in
仅包含了以字符串为键的属性Object.keys
仅包含了对象自身的、可枚举的、以字符串为键的属性Object.getOwnPropertyNames
包含了所有以字符串为键的属性,即使是不可枚举的Object.getOwnPropertySymbols
与前者类似,但其包含的是以 Symbol 为键的属性- 等等
- 对象可以实现迭代协议,或者你可以使用
Object.keys
或Object.entries
。 for...in
表达式允许你迭代一个对象的可枚举属性。
Arguments 对象
在js中,我们在调用有参数的函数时,当传参时,js会把所有的参数都存到一个叫 arguments
的对象中,它是一个类数组数据。
“类数组” 意味着 arguments
有 长度
属性 并且属性的索引是从零开始的,但是它没有 Array
的 内置方法, 例如 forEach()
和 map()
都是没有的。但是它可以被转换为一个真正的Array:
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);
// ES2015
const args = Array.from(arguments);
const args = [...arguments];
arguments对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments
对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引 0 处。
如果调用的参数多于正式声明接受的参数,则可以使用arguments
对象。这种技术对于可以传递可变数量的参数的函数很有用。使用 arguments.length来确定传递给函数参数的个数,然后使用arguments
对象来处理每个参数。要确定函数签名中(输入)参数的数量,请使用Function.length
属性。
在ES6
,除了 Arguments
,还可以使用剩余参数:
function any(a, b, ...theArgs) {
// ...
}
instanceof的实现原理及手写instanceof的实现
在JavaScript中,检测一个变量的类型,有以下三种方法:
typeof
Object.prototype.toString.call()
instanceof
- 判断一个实例是否属于某个类型
instanceof 的实现原理:
instanceof
运算符用于测试构造函数的prototype
属性是否出现在对象原型链中的任何位置。
知道原理就好办了,一直往下查询即可:
function isInstanceOf(child, parent) {
let pt = parent.prototype;
let ct = child.__proto__;
while(true) {
if(ct === null) {
return false;
}
if(ct === pt) {
return true;
}
ct = ct.__proto__;
}
}
js原型链
prototype
__proto__
constructor
参考文章:js原型链
编码和字符集的区别
字符集是书写系统字母与符号的集合,而字符编码则是将字符映射为一特定的字节或字节序列,是一种规则。 通常特定的字符集采用特定的编码方式(即一种字符集对应一种字符编码(例如:ASCII、IOS-8859-1、 GB2312、GBK,都是即表示了字符集又表示了对应的字符编码,但Unicode不是,它采用现代的模型))
- 字符:在计算机和电信技术中,一个字符是一个单位的字形、类字形单位或符号的基本信息。即一个字符可以是一个中文汉字、一个英文字母、一个阿拉伯数字、一个标点符号等。
- 字符集:多个字符的集合。例如GB2312是中国国家标准的简体中文字符集,GB2312收录简化汉字(6763个) 及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母,共 7445 个图形字符。
- 字符编码:把字符集中的字符编码为(映射)指定集合中的某一对象,以便文本在计算机中存储和通过通信网络的传递。
null 和 undefined
undefined 的字面意思就是:未定义的值 。这个值的语义是,希望表示一个变量最原始的状态,而非人为操作 的结果 。 这种原始状态会在以下 4 种场景中出现:
- 声明了一个变量,但没有赋值
- 访问对象上不存在的属性
- 函数定义了形参,但没有传递实参
- 使用 void 对表达式求值
你也可以手动给一个变量赋值 undefined,但这样做没有意义,因为一个变量不赋值就是 undefined。
null 的字面意思是:空值 。这个值的语义是,希望表示 一个对象被人为的重置为空对象,而非一个变量最原始的状态 。在内存里的表示就是,栈中的变量没有指向堆中的内存对象 null 有属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数 据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进 制位恰好都是 0 ,因此,null 被误判断为 Object 类型。
数组和类数组的区别
数组是一个特殊对象,与常规对象的区别:
- 当由新元素添加到列表中时,自动更新length属性
- 设置
length
属性,可以截断数组 - 从
Array.protoype
中继承了方法 - 属性为
Array
类数组是一个拥有length
属性,并且他属性为非负整数的普通对象,类数组不能直接调用数组方法。
类数组是简单对象,它的原型关系与数组不同。
类数组转换为数组
- 使用
Array.from()
- 使用
Array.prototype.slice.call()
- 使用
Array.prototype.forEach()
进行属性遍历并组成新的数组
转换后的数组长度由 length 属性决定。索引不连续时转换结果是连续的,会自动补位 且转换仅考虑 0或正整数 的索引
转换示例:
let arr = {
length: 4,
'-1': -1,
'0': 0,
a: 'a',
1: 1
};
console.log(Array.from(arr)); // [ 0, 1, undefined, undefined ]
console.log(Array.prototype.slice.call(arr)) // [ 0, 1, <2 empty items> ] 稀疏数组
类数组尽管不能直接调用数组的方法,但是可以间接的使用Function.call
方法调用; 或者使用数组方法的静态函数版本。
var a = {"0": "a", "1": "b", "2": "c", length: 3}; // 类数组对象
Array.prototype.join.call(a, "+"); // => "a+b+c"
Array.prototype.slice.call(a, 0); // => ["a", "b", "c"]
Array.prototype.map.call(a, function(x) {
return x.toUpperCase(); // => ["A", "B", "C"]
})
// -----
Array.join(a, "+");
Array.slice(a, 0);
Array.map(a, function(x) {
return x.toUpperCase();
})
扁平数组和JSON树的转换
扁平数组 转化为 JSON 树
let flatArr = [
{ id: 1, title: "title1", parent_id: 0 },
{ id: 2, title: "title2", parent_id: 0 },
{ id: 3, title: "title3", parent_id: 2 },
{ id: 4, title: "title4", parent_id: 3 },
{ id: 5, title: "title5", parent_id: 4 },
{ id: 6, title: "title6", parent_id: 3 },
];
function arr2json(arr) {
let ans = [];
const map = arr.reduce((pre, cur) => {
pre[cur.id] = Object.assign({}, cur);
return pre;
}, {});
for(const node of arr) {
if(node.parent_id === 0) {
ans.push(map[node.id]);
}
if(node.parent_id in map) {
const parent = map[node.parent_id];
if(parent.children) {
parent.children.push(map[node.id]);
} else {
parent.children = [map[node.id]];
}
}
}
return ans;
}
const json = arr2json(flatArr)
/*
json = [
{ id: 1, title: 'title1', parent_id: 0 },
{
id: 2,
title: 'title2',
parent_id: 0,
children: [
{
id: 3,
title: 'title3',
parent_id: 2,
children: [
{
id: 4,
title: 'title4',
parent_id: 3,
children: [
{
id: 5,
title: 'title5',
parent_id: 4
}
]
},
{ id: 6, title: 'title6', parent_id: 3 }
]
}
]
}
]
*/
JSON 树 转化为 扁平数组
// JSON 树 -> 扁平数组
function json2FlatArr(json) {
return json.reduce((pre, cur) => {
const {id, title, parent_id, children = []} = cur;
pre.push({
id,
title,
parent_id
})
return [...pre, ...json2FlatArr(children)];
}, [])
}
json2FlatArr(json)
Set
、Map
、WeakSet
和 WeakMap
的区别
Set
:
- 成员不能重复;
- 只有键值,没有键名,有点类似数组;
- 可以遍历,方法有 add、delete、has
WeakSet
:
- 成员都是对象(引用);
- 成员都是弱引用,随时可以消失(不计入垃圾回收机制)。可以用来保存 DOM 节点,不容易造成内存 泄露;
- 不能遍历,方法有 add、delete、has;
Map
:
- 本质上是键值对的集合,类似集合;
- 可以遍历,方法很多,可以跟各种数据格式转换;
WeakMap
:
- 只接收对象为键名(null 除外),不接受其他类型的值作为键名;
- 键名指向的对象,不计入垃圾回收机制;
- 不能遍历,方法同 get、set、has、delete;
js 中有哪几种内存泄露的情况
- 意外的全局变量;
- 闭包;
- 未被清空的定时器;
- 未被销毁的事件监听;
- DOM 引用;
异步执行顺序
查看下面代码输出顺序:
console.log("start")
setTimeout(() => {
console.log("setTimeout")
},0)
async function asy1() {
console.log("asy1 start")
await asy2()
console.log("asy1 end")
}
async function asy2() {
console.log("asy2 start")
}
asy1()
new Promise((res) => {
console.log('promise')
res();
}).then(() => {
console.log("promise then")
})
console.log("end!")
答案:
start
asy1 start
asy2 start
promise
end!
asy1 end
promise then
setTimeout
json
和xml
数据的区别
- 数据体积方面:
xml
是重量级的,json
是轻量级的,传递的速度更快些。 - 数据传输方面:
xml
在传输过程中比较占带宽,json
占带宽少,易于压缩。 - 数据交互方面:
json
与javascript
的交互更加方便,更容易解析处理,更好的进行数据交互 - 数据描述方面:
json
对数据的描述性比xml
较差 xml
和json
都用在项目交互下,xml
多用于做配置文件,json
用于数据交互
delete
delete 操作符用来删除一个对象的属性。
delete
在删除一个不可配置的属性时在严格模式和非严格模式下的区别:- 在严格模式中,如果属性是一个不可配置(
non-configurable
)属性,删除时会抛出异常; - 非严格模式下返回
false
。
- 在严格模式中,如果属性是一个不可配置(
delete
能删除隐式声明的全局变量:这个全局变量其实是global
对象(window
)的属性delete
能删除的- 可配置对象的属性
- 隐式声明的全局变量
- 用户定义的属性
- 在
ECMAScript 6
中,通过const
或let
声明指定的"temporal dead zone" (TDZ)
对delete
操作符也会起作用
delete
不能删除的- 显式声明的全局变量
- 内置对象的内置属性
- 一个对象从原型继承而来的属性
delete
删除数组元素- 当你删除一个数组元素时,数组的
length
属性并不会变小,数组元素变成undefined
- 当用
delete
操作符删除一个数组元素时,被删除的元素已经完全不属于该数组。 - 如果你想让一个数组元素的值变为
undefined
而不是删除它,可以使用undefined
给其赋值而不是使用delete
操作符。此时数组元素是在数组中的
- 当你删除一个数组元素时,数组的
delete
操作符与直接释放内存(只能通过解除引用来间接释放)没有关系