(1) audio:音频
<audio src='' controls autoplay loop='true'></audio>
属性:
(2)video视频
<video src='' poster='imgs/aa.jpg' controls></video>
属性:
(3)source标签
因为浏览器对视频格式支持程度不一样,为了能够兼容不同的浏览器,可以通过source来指定视频源。
<video>
<source src='aa.flv' type='video/flv'></source>
<source src='aa.mp4' type='video/mp4'></source>
</video>
表单类型:
表单属性:
表单事件:
设置规则:min < low < high < max
它们选择的对象可以是标签,可以是类(需要加点),可以是ID(需要加#)
HTML5 提供了两种在客户端存储数据的新方法:
<img draggable="true" />
<canvas id="myCanvas" width="200" height="100"></canvas>
总结: (1)新增语义化标签:nav、header、footer、aside、section、article
(2)音频、视频标签:audio、video
(3)数据存储:localStorage、sessionStorage
(4)canvas(画布)、Geolocation(地理定位)、websocket(通信协议)
(5)input标签新增属性:placeholder、autocomplete、autofocus、required
(6)history API:go、forward、back、pushstate
移除的元素有:
浅克隆:
function shallowClone(obj) {
let cloneObj = {};
for (let i in obj) {
cloneObj[i] = obj[i];
}
return cloneObj;
}
深克隆:
function deepCopy(obj) {
if (typeof obj === 'object') {
var result = obj.constructor === Array ? [] : {};
for (var i in obj) {
result[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
}
} else {
var result = obj;
}
return result;
}
服务器为了提高网站访问速度,对之前访问的部分页面指定缓存机制,当客户端在此对这些页面进行请求,服务器会根据缓存内容判断页面与之前是否相同,若相同便直接返回304,此时客户端调用缓存内容,不必进行二次下载。
状态码304不应该认为是一种错误,而是对客户端有缓存情况下服务端的一种响应。
搜索引擎蜘蛛会更加青睐内容源更新频繁的网站。通过特定时间内对网站抓取返回的状态码来调节对该网站的抓取频次。若网站在一定时间内一直处于304的状态,那么蜘蛛可能会降低对网站的抓取次数。相反,若网站变化的频率非常之快,每次抓取都能获取新内容,那么日积月累,的回访率也会提高。
产生较多304状态码的原因:
304状态码出现过多会造成以下问题:
var a, b
(function () {
console.log(a);
console.log(b);
var a = (b = 3);
console.log(a);
console.log(b);
})()
console.log(a);
console.log(b);
输出结果:
undefined
undefined
3
3
undefined
3
这个题目和上面题目考察的知识点类似,b赋值为3,b此时是一个全局变量,而将3赋值给a,a是一个局部变量,所以最后打印的时候,a仍旧是undefined。
是基于原型的动态语言,主要独特特性有 this、原型和原型链。
JS 严格意义上来说分为:语言标准部分(ECMAScript)+ 宿主环境部分
语言标准部分
2015 年发布 ES6,引入诸多新特性使得能够编写大型项目变成可能,标准自 2015 之后以年号代号,每年一更
宿主环境部分
function foo() {
console.log( this.a );
}
function doFoo() {
foo();
}
var obj = {
a: 1,
doFoo: doFoo
};
var a = 2;
obj.doFoo()
输出结果:2
在Javascript中,this指向函数执行时的当前对象。在执行foo的时候,执行环境就是doFoo函数,执行环境为全局。所以,foo中的this是指向window的,所以会打印出2。
vue-router是vuex.js官方的路由管理器,它和vue.js的核心深度集成,让构建但页面应用变得易如反掌
<router-link> 组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址
<router-view> 组件是一个 functional 组件,渲染路径匹配到的视图组件。
<keep-alive> 组件是一个用来缓存组件
router.beforeEach
router.afterEach
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
介绍了路由守卫及用法,在项目中路由守卫起到的作用等等
浏览器端常用的存储技术是 cookie 、localStorage 和 sessionStorage。
上面几种方式都是存储少量数据的时候的存储方式,当需要在本地存储大量数据的时候,我们可以使用浏览器的 indexDB 这是浏览器提供的一种本地的数据库存储机制。它不是关系型数据库,它内部采用对象仓库的形式存储数据,它更接近 NoSQL 数据库。
这两种方式都是提高网页性能的方式,两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
对于如何优化动画,我们知道,一般情况下,动画需要频繁的操作DOM,就就会导致页面的性能问题,我们可以将动画的position
属性设置为absolute
或者fixed
,将动画脱离文档流,这样他的回流就不会影响到页面了。
离线存储指的是:在用户没有与因特网连接时,可以正常访问站点或应用,在用户与因特网连接时,更新用户机器上的缓存文件。
原理:HTML5的离线存储是基于一个新建的 .appcache
文件的缓存机制(不是存储技术),通过这个文件上的解析清单离线存储资源,这些资源就会像cookie一样被存储了下来。之后当网络在处于离线状态下时,浏览器会通过被离线存储的数据进行页面展示
使用方法: (1)创建一个和 html 同名的 manifest 文件,然后在页面头部加入 manifest 属性:
<html lang="en" manifest="index.manifest">
(2)在 cache.manifest
文件中编写需要离线存储的资源:
CACHE MANIFEST
#v0.11
CACHE:
js/app.js
css/style.css
NETWORK:
resourse/logo.png
FALLBACK:
/ /offline.html
(3)在离线状态时,操作 window.applicationCache
进行离线缓存的操作。
如何更新缓存:
(1)更新 manifest 文件
(2)通过 javascript 操作
(3)清除浏览器缓存
注意事项:
(1)浏览器对缓存数据的容量限制可能不太一样(某些浏览器设置的限制是每个站点 5MB)。
(2)如果 manifest 文件,或者内部列举的某一个文件不能正常下载,整个更新过程都将失败,浏览器继续全部使用老的缓存。
(3)引用 manifest 的 html 必须与 manifest 文件同源,在同一个域下。
(4)FALLBACK 中的资源必须和 manifest 文件同源。
(5)当一个资源被缓存后,该浏览器直接请求这个绝对路径也会访问缓存中的资源。
(6)站点中的其他页面即使没有设置 manifest 属性,请求的资源如果在缓存中也从缓存中访问。
(7)当 manifest 文件发生改变时,资源请求本身也会触发更新。
var A = {n: 4399};
var B = function(){this.n = 9999};
var C = function(){var n = 8888};
B.prototype = A;
C.prototype = A;
var b = new B();
var c = new C();
A.n++
console.log(b.n);
console.log(c.n);
输出结果:9999 4400
解析:
浏览器渲染主要有以下步骤:
大致过程如图所示:
注意: 这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html 都解析完成之后再去构建和布局 render 树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
// a
function Foo () {
getName = function () {
console.log(1);
}
return this;
}
// b
Foo.getName = function () {
console.log(2);
}
// c
Foo.prototype.getName = function () {
console.log(3);
}
// d
var getName = function () {
console.log(4);
}
// e
function getName () {
console.log(5);
}
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
new new Foo().getName(); // 3
输出结果:2 4 1 1 2 3 3
解析:
首先渲染的前提是生成渲染树,所以 HTML 和 CSS 肯定会阻塞渲染。如果你想渲染的越快,你越应该降低一开始需要渲染的文件大小,并且扁平层级,优化选择器。然后当浏览器在解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都建议将 script 标签放在 body 标签底部的原因。
当然在当下,并不是说 script 标签必须放在底部,因为你可以给 script 标签添加 defer 或者 async 属性。当 script 标签加上 defer 属性以后,表示该 JS 文件会并行下载,但是会放到 HTML 解析完成后顺序执行,所以对于这种情况你可以把 script 标签放在任意位置。对于没有任何依赖的 JS 文件可以加上 async 属性,表示 JS 文件下载和解析不会阻塞渲染。
JavaScript 使用 Number 类型来表示数字(整数或浮点数),遵循 IEEE 754 标准,通过 64 位来表示一个数字(1 + 11 + 52)
最大安全数字:Number.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1,转换成整数就是 16 位,所以 0.1 === 0.1,是因为通过 toPrecision(16) 去有效位之后,两者是相等的。
在两数相加时,会先转换成二进制,0.1 和 0.2 转换成二进制的时候尾数会发生无限循环,然后进行对阶运算,JS 引擎对二进制进行截断,所以造成精度丢失。
所以总结:精度丢失可能出现在进制转换和对阶运算中
ES6 提供了新的数据结构 Set。
它类似于数组,但是成员的值都是唯一的,没有重复的值。 Set 本身是一个构造函数,用来生成 Set 数据结构。
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
1.props和$emit
2.中央事件总线 EventBus(基本不用)
3.vuex(官方推荐状态管理器)
4.$parent和$children
当然还有一些其他办法,但基本不常用,或者用起来太复杂来。 介绍来通信的方式,还可以扩展说一下使用
场景,如何使用,注意事项之类的。
应该有面试官问过你:
这些问题其实都可以被看作是同一个问题,那就是面试官在问你:你对JS闭包了解多少?
来总结一下我听到过的答案,尽量完全复原候选人面试的时候说的原话。
答案1:
就是一个function
里面return
了一个子函数,子函数访问了外面那个函数的变量。
答案2:
for循环里面可以用闭包来解决问题。
for(var i = 0; i < 10; i++){
setTimeout(()=>console.log(i),0)
}
// 控制台输出10遍10.
for(var i = 0; i < 10; i++){
(function(a){
setTimeout(()=>console.log(a),0)
})(i)
}
// 控制台输出0-9
答案3:
当前作用域产产生了对父作用域的引用。
答案4:
不知道。是跟浏览器的垃圾回收机制有关吗?
开杠了。请问,小伙伴的答案和以上的内容有多少相似程度?
其实,拿着这些问题好好想想,你就会发现这些问题都只是为了最终那一个问题。
闭包的底层实现原理
1. JS执行上下文
我们都知道,我们手写的js代码是要经过浏览器V8进行预编译后才能真正的被执行。例如变量提升、函数提升。举个栗子。
// 栗子:
var d = 'abc';
function a(){
console.log("函数a");
};
console.log(a); // ƒ a(){ console.log("函数a"); }
a(); // '函数a'
var a = "变量a";
console.log(a); // '变量a'
a(); // a is not a function
var c = 123;
// 输出结果及顺序:
// ƒ a(){ console.log("函数a"); }
// '函数a'
// '变量a'
// a is not a function
// 栗子预编后相当于:
function a(){
console.log("函数a");
};
var d;
console.log(a); // ƒ a(){ console.log("函数a"); }
a(); // '函数a'
a = "变量a"; // 此时变量a赋值,函数声明被覆盖
console.log(a); // "变量a"
a(); // a is not a function
那么问题来了。 请问是谁来执行预编译操作的?那这个谁又是在哪里进行预编译的?
是的,你的疑惑没有错。js代码运行需要一个运行环境,那这个环境就是执行上下文。 是的,js运行前的预编译也是在这个环境中进行。
js执行上下文分三种:
全局执行上下文
: 代码开始执行时首先进入的环境。函数执行上下文
:函数调用时,会开始执行函数中的代码。eval执行上下文
:不建议使用,可忽略。那么,执行上下文的周期,分为两个阶段:
创建阶段
VO
),建立作用域链、作用域链、作用域链(重要的事说三遍)this
指向,并绑定this
执行阶段
。这个阶段进行变量赋值,函数引用及执行代码。你现在猜猜看,预编译是发生在什么时候?
噢,我忘记说了,其实与编译还有另一个称呼:执行期上下文
。
预编译发生在函数执行之前。预编译四部曲为:
AO
对象undefined
AO
对象中拿。所以,预编译真正的结果是:
var AO = {
a = function a(){console.log("函数a");};
d = 'abc'
}
我们重新来。
变量对象是 js
代码在进入执行上下文时,js
引擎在内存中建立的一个对象,用来存放当前执行环境中的变量。
变量对象的创建,是在执行上下文创建阶段,依次经过以下三个过程:
arguments
对象。 对于函数执行环境,首先查询是否有传入的实参,如果有,则会将参数名是实参值组成的键值对放入arguments
对象中。否则,将参数名和 undefined
组成的键值对放入 arguments
对象中。
//举个栗子
function bar(a, b, c) {
console.log(arguments); // [1, 2]
console.log(arguments[2]); // undefined
}
bar(1,2)
console.log(a); // function a() {console.log('Is a ?') }
function a() {
console.log('Is a');
}
function a() {
console.log('Is a ?')
}
/**ps: 在执行第一行代码之前,函数声明已经创建完成.后面的对之前的声明进行了覆盖。**/
undefined
。当遇到同名的函数声明,为了避免函数被赋值为 undefined
,会忽略此声明console.log(a); // function a() {console.log('Is a ?') }
console.log(b); // undefined
function a() {
console.log('Is a ');
}
function a() {
console.log('Is a ?');
}
var b = 'Is b';
var a = 10086;
/**这段代码执行一下,你会发现 a 打印结果仍旧是一个函数,而 b 则是 undefined。**/
根据以上三个步骤,对于变量提升也就知道是怎么回事了。
执行上下文的第二个阶段,称为执行阶段,在此时,会进行变量赋值,函数引用并执行其他代码,此时,变量对象变为活动对象。
我们还是举上面的例子:
console.log(a); // function a() {console.log('fjdsfs') }
console.log(b); // undefined
function a() {
console.log('Is a');
}
function a() {
console.log('Is a?');
}
var b = 'Is b';
console.log(b); // 'Is b'
var a = 10086;
console.log(a); // 10086
var b = 'Is b?';
console.log(b); // 'Is b?'
在上面的代码中,代码真正开始执行是从第一行 console.log() 开始的,自这之前,执行上下文是这样的:
// 创建过程
EC= {
VO: {}; // 创建变量对象
scopeChain: {}; // 作用域链
}
VO = {
argument: {...}; // 当前为全局上下文,所以这个属性值是空的
a: <a reference> // 函数 a 的引用地址 b: undefiend // 见上文创建变量对象的第三步}
Lexical scope
)这里想说明,我们在函数执行上下文中有变量,在全局执行上下文中有变量。JavaScript
的一个复杂之处在于它如何查找变量,如果在函数执行上下文中找不到变量,它将在调用上下文中寻找它,如果在它的调用上下文中没有找到,就一直往上一级,直到它在全局执行上下文中查找为止。(如果最后找不到,它就是 undefined
)。
再来举个栗子:
1: let top = 0; //
2: function createWarp() {
3: function add(a, b) {
4: let ret = a + b
5: return ret
6: }
7: return add
8: }
9: let sum = createWarp()
10: let result = sum(top, 8)
11: console.log('result:',result)
分析过程如下:
top
并赋值为0.createWarp
的变量,并为其分配了一个函数定义。其中第3-7行描述了其函数定义,并将函数定义存储到那个变量(createWarp
)中。sum
的新变量,暂时,值为 undefined
。()
,表明需要执行或调用一个函数。那么查找全局执行上下文的内存并查找名为 createWarp
的变量。 明显,已经在步骤2中创建完毕。接着,调用它。createWarp
执行上下文。我们可以在 createWarp
的执行上下文中创建自有变量。js
引擎createWarp
的上下文添加到调用堆栈(call stack
)。因为这个函数没有参数,直接跳到它的主体部分.createWarp
执行上下文中创建一个变量 add
。add
只存在于 createWarp
执行上下文中, 其函数定义存储在名为 add
的自有变量中。add
的内容。js引擎查找一个名为 add
的变量并找到它. 第4行和第5行括号之间的内容构成该函数定义。createWarp
调用完毕,createWarp
执行上下文将被销毁。add 变量也跟着被销毁。 但 add
函数定义仍然存在,因为它返回并赋值给了 sum
变量。 (ps: 这才是闭包产生的变量存于内存当中的真相
)小结一下
现在,如果再让你回答什么是闭包,你能答出多少?
其实,大家说的都对。不管是函数返回一个函数,还是产生了外部作用域的引用,都是有道理的。
所以,什么是闭包?
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。