1.起源
JavaScript 起源于 1995 年,当时它主要是为了满足网页交互的需求而被创建。它最初的设计目的是为了让网页开发者能够在网页中添加一些简单的交互效果和动态内容。在那个时期,网页大多是静态的,而 JavaScript 的出现为网页带来了新的活力。Netscape 公司的 Brendan Eich 负责了 JavaScript 的开发工作。起初,它的名字是 LiveScript ,后来为了借助 Java 语言的热度,更名为 JavaScript 。例如,早期的 JavaScript 可以用于验证表单输入,当用户提交表单时,能够在客户端立即检查输入是否符合要求,而无需将数据发送到服务器进行验证,节省了时间和资源。另外,它还可以用于在网页上显示动态的日期和时间,或者根据用户的操作显示或隐藏某些元素。随着时间的推移,JavaScript 不断发展壮大,功能越来越丰富,应用场景也越来越广泛,从简单的网页交互扩展到了 Web 应用开发、服务器端编程(如 Node.js )、移动应用开发等众多领域。
2.ECMAScript
很多同学可能跟着网上学习,学到最后看到别人公司要求熟悉ES6,一头雾水,ES6是什么?
ES6是ECMAScript 6的简称,是JavaScript的一种规范.
3.javascript数据类型(基本数据类型+引用数据类型)
这个知识点很多人觉得轻轻松松,但是有时候又被面试官问的哑口无言.
基本数据类型
javascript常见的六种数据类型:
number(数值型),string(字符串型) ,boolean(布尔型),
undefined(当声明变量没有初始化时),null, symbol
null
逻辑上讲,null 值表示一个空对象指针
//区别null和undfined
console.log(1+null)//1
console.log(1+undefined)//NAN(not a number)
数undefined 值是由 null 值派生而来的,因此 ECMA-262 将它们定义为表面上相等
Symbol(ES6)
Symbol(符号)是 ECMAScript 6 新增的数据类型。符号是原始值,且符号实例是唯一、不可变的。 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险
Symbol.for
如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册 表中创建并重用符号
let sym = Symbol.for('test'); //使用test字符串调用Symbol.for() 生成一个新符号实例并添加到注册表中,后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例
let sym1=Symbol.for('test')//重用已有符号
Symbol.keyFor()
查询全局注册表
数据转换
转为string类型:toString(),String()
转为number类型:Number()、parseInt() 和parseFloat()。parseInt()将字符串转换为整数,parseFloat()将字符串转换为浮点数
转为boolean类型:Boolean()
不同类型与布尔值之间的转换规则。
<script>
//转为字符串
let a = 123
a = a.toString();
console.log(typeof a);
let b = 123
b = String(b);
console.log(typeof b);
//转为数值
let c = '123'
c=Number(c)
console.log(typeof c)
let d='123.123'
d=Number(d)
console.log(typeof d)
//转为布尔
let e='true'
e=Boolean(e)
console.log(typeof e)
</script>
引用数据类型
Object 类型
是一组由键、值组成的无序集合,定义对象类型需要使用花括号{ }
在单个容器中收集多个值的一种常见方法是使用对象。对象是键/值对的集合。JS 中还有一些具有专门行为的对象子类型,例如数组(数字索引)甚至函数(可调用);
注意: |
---|
键通常称为“属性名称”,属性名称和值的配对通常称为“属性”。 |
创建对象(对象字面量(object literal)表示法)
Array 类型
数组(Array)是一组按顺序排列的数据的集合,数组中的每个值都称为元素,而且数组中可以
包含任意类型的数据。在 JavaScript 中定义数组需要使用方括号[ ]
,数组中的每个元素使用逗号进行分隔,数组属于一种特殊的对象
ECMAScript 提供了 Array.isArray()方法。这个方法的目的就是确定一个值是 否为数组,而不用管它是在哪个全局执行上下文中创建的。
Array 构造函数还有两个 ES6 新增的用于创建数组的静态方法:from()和 of()。from()用于将 类数组结构转换为数组实例,而 of()用于将一组参数转换为数组实例。 Array.from()的第一个参数是一个类数组对象,即任何可迭代的结构,或者有一个 length 属性 和可索引元素的结构。
数组方法
改变原数组(7)
push()方法:该方法可以向数组的末尾添加一个或多个元素,并返回数组的新的长度
pop()方法:该方法可以删除数组的最后一个元素,并将被删除的元素作为返回值返回
unshift()方法:该方法向数组开头添加一个或多个元素,并返回新的数组长度
shift()方法:该方法可以删除数组的第一个元素,并将被删除的元素作为返回值返回
sort()方法:该方法可以用来对数组中的元素进行排序,也会影响原数组,默认会按照Unicode编码进行排序
reverse()方法:该方法用来反转数组(前边的去后边,后边的去前边),该方法会直接修改原数组
splice()方法:该方法可以用于删除数组中的指定元素,该方法会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回
参数:
- 第一个参数:表示开始位置的索引
- 第二个参数:表示要删除的元素数量
- 第三个参数及以后参数:可以传递一些新的元素,这些元素将会自动插入到开始位置索引后边
fill():用给定值填充一个数组
参数
value 必需。填充的值。
start 可选。开始填充位置。
end 可选。停止填充位置 (既不包含最后一个)(默认为 array.length)
不改变原数组(13)
forEach()方法:该方法可以用来遍历数组(for语句的特殊简化版本)
forEach()方法需要一个函数作为参数,像这种函数,由我们创建但是不由我们调用的,我们称为回调函数。数组中有几个元素函数就会执行几次,每次执行时,浏览器会将遍历到的元素,以实参的形式传递进来,我们可以来定义形参,来读取这些内容,浏览器会在回调函数中传递三个参数:
第一个参数:就是当前正在遍历的元素
第二个参数:就是当前正在遍历的元素的索引
第三个参数:就是正在遍历的数组
map(): 遍历数组, 每次循环时执行传入的回调函数,根据回调函数的返回值,生成一个新的数组 ,
同forEach() 方法,但是map()方法有返回值,可以return出来
Set()ES6->->一般涉及到算法
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:
size:返回集合的元素个数
add():增加一个新元素,返回当前集合(add()
方法向 Set
结构加入成员, Set
结构不会添加重复的值。)
delete():删除元素,返回 boolean 值
has():检测集合中是否包含某个元素,返回 boolean 值
clear():清空集合,返回 undefined
Map()ES6->一般涉及到算法
ES6 提供了 Map 数据结构。它类似于对象
- size:返回 Map 的元素个数
- set():增加一个新元素,返回当前 Map
- get():返回键名对象的键值
- has():检测 Map 中是否包含某个元素,返回 boolean 值
- clear():清空集合,返回 undefined
也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map 的属性和方法:
filter(): 遍历数组, 每次循环时执行传入的回调函数,回调函数返回一个条件,把满足条件的元素筛选出来放到新数组中.
every(): 遍历数组, 每次循环时执行传入的回调函数,回调函数返回一个条件,全都满足返回true 只要有一个不满足 返回false => 判断数组中所有的元素是否满足某个条件
some(): 遍历数组, 每次循环时执行传入的回调函数,回调函数返回一个条件,只要有一个元素满足条件就返回true,都不满足返回false => 判断数组中是否存在,满足某个条件的元素
includes(): 用来判断一个数组是否包含一个指定的值,如果是返回 true,否则false。(严格按照===判断)
find(): 遍历数组 每次循环 执行回调函数,回调函数接受一个条件 返回满足条件的第一个元素,不存在则返回undefined
findIndex():是传入一个测试条件,也就是函数,找到了返回当前项索引,没有找到返回-1. 属于ES6
该方法可快速查找对象数组满足条件的索引,indexOf不支持
let arr = [{ id: 1, name: 'coco' }, { id: 2, name: 'dudu' }]
let res = arr.findIndex(item => item.id == 1)
console.log('res) // 0
indexOf():查询某个元素在数组中第一次出现的位置 存在该元素,返回下标,不存在 返回 -1 (可以通过返回值 变相的判断是否存在该元素),(ES5)
lastIndexOf(): 查询某个元素在数组中最后一次出现的位置 (或者理解为反向查询第一次出现的位置) 存在该元素,返回下标,不存在 返回 -1 (可以通过返回值 变相的判断是否存在该元素)
slice():该方法可以用来从数组提取指定元素,该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回(slice->片,切片)
- 第一个参数:截取开始的位置的索引,包含开始索引
- 第二个参数:截取结束的位置的索引,不包含结束索引,第二个参数可以省略不写,此时会截取从开始索引往后的所有元素
注意:索引可以传递一个负值,如果传递一个负值,则从后往前计算,-1代表倒数第一个,-2代表倒数第二个。
join()方法演示:该方法可以将数组转换为一个字符串,该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回,在join()中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符,如果不指定连接符,则默认使用 ,作为连接符
concat()方法演示:该方法可以连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响
面试被问到过的找到元素返回下标:
Function 类型
函数(Function)是一段具有特定功能的代码块,函数并不会自动运行,需要通过函数名调用才能运行
确定类型
typeof 虽然对原始值很有用,但它对引用值的用处不大。我们通常不关心一个值是不是对象, 而是想知道它是什么类型的对象。为了解决这个问题,ECMAScript 提供了 instanceof 操作符,语 法如下:
result = variable instanceof constructor
如果变量是给定引用类型的实例,则 instanceof 操作 符返回 true。来看下面的例子: console.log(person instanceof Object); // 变量 person 是 Object 吗?
console.log(colors instanceof Array); // 变量 colors 是 Array 吗?
console.log(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗?
按照定义,所有引用值都是 Object 的实例,因此通过 instanceof 操作符检测任何引用值和 Object 构造函数都会返回 true。类似地,如果用 instanceof 检测原始值,则始终会返回 false, 因为原始值不是对象
3.变量、作用域与内存
原始值(primitive value)就是 最简单的数据,引用值(reference value)则是由多个值构成的对象。保存原始值的变量是按值(by value)访问的,因为我们操作的就是存储在变量中的实际值。 引用值是保存在内存中的对象。与其他语言不同,JavaScript 不允许直接访问内存位置,因此也就 不能直接操作对象所在的内存空间。在操作对象时,实际上操作的是对该对象的引用(reference)而非实际的对象本身。为此,保存引用值的变量是按引用(by reference)访问的
1)栈(先进后出)和堆
JavaScript在运行时数据是保存到栈内存和堆内存当中的。
简单来说栈内存用来保存变量和基本类型,堆内存是用来保存对象。
我们在声明一个变量时,实际上就是在栈内存中创建了一个空间用来保存变量。
如果是基本类型则在栈内存中直接保存,如果是引用类型则会在堆内存中保存,变量中保存的实际上对象在堆内存中的地址。
我们来看一下差别:
注意 ECMAScript 中函数的参数就是局部变量。
2)上下文
3)作用域
全局作用域
直接编写在script标签中的JavaScript代码,都在全局作用域
全局作用域在页面打开时创建,在页面关闭时销毁
在全局作用域中有一个全局对象window,它代表的是一个浏览器的窗口,它由浏览器创建,我们可以直接使用
在全局作用域中:
创建的变量都会作为window对象的属性保存
创建的函数都会作为window对象的方法保存
全局作用域中的变量都是全局变量,在页面的任意的部分都可以访问的到
函数作用域
调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
每调用一次函数就会创建一个新的函数作用域,它们之间是互相独立的
在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
在函数中要访问全局变量可以使用window对象
作用域链:当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用,如果没有则向上一级作用域中寻找,直到找到全局作用域,如果全局作用域中依然没有找到,则会报错ReferenceError
作用域链
多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外),查找变量时就是沿着作用域链来查找的。
查找一个变量的查找规则:
在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3
再次执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的ReferenceError异常
4)闭包(重要哦)
有基础的同学可以把闭包类比java和c++的私有变量哦!
function createUser(name) {
let userName = name; // 私有变量
return {
getName: function() {
return userName; // 访问私有变量
},
setName: function(newName) {
userName = newName; // 修改私有变量
}
};
}
const user = createUser('Alice');
console.log(user.getName()); // 输出 'Alice'
user.setName('Bob');
console.log(user.getName()); // 输出 'Bob'
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i); // 输出 5,因为 `i` 是在 for 循环结束后才被访问的
}, 1000);
}
// 使用闭包修复这个问题
for (var j = 0; j < 5; j++) {
(function(index) {
setTimeout(function() {
console.log(index); // 输出 0, 1, 2, 3, 4
}, 1000);
})(j);
}
#### 执行过程:
1. `for` 循环从 `j = 0` 开始,直到 `j < 5` 的条件不再满足。
2. 在每次循环中,立即调用一个匿名函数,并将当前的 `j` 值(即 `index`)作为参数传入。
3. 这个匿名函数创建了一个新的作用域,并在其中定义了 `index` 变量。
4. `setTimeout` 注册的回调函数引用的是 `index`,而不是外部的 `j` 变量。
5. 因此,当回调函数在 1 秒后执行时,它会输出传入的 `index` 值,分别为 `0, 1, 2, 3, 4`。
#### 作用域:
- 通过使用闭包,每次循环都创建了一个新的作用域,`index` 变量在每次迭代中都是独立的。
### 总结
- 第一个代码片段由于使用了 `var` 声明的变量,导致所有的回调函数共享同一个 `i` 变量,最终输出相同的值 `5`。
- 第二个代码片段通过闭包的方式,确保每个回调函数都能访问到其各自的 `index` 值,从而输出 `0, 1, 2, 3, 4`。
这种差异展示了 JavaScript 中作用域和闭包的重要性,尤其是在处理异步操作时。
如何产生闭包?
当一个函数在另一个函数内部被定义时,它会形成一个闭包。这个内嵌函数可以访问外部函数的变量,即使外部函数已经执行完毕。
闭包的特性
- 保持对外部作用域变量的引用:闭包可以访问声明在其外部的变量。
- 保护变量不被外部修改:通过封闭的作用域来提供私有变量的能力。
闭包生命周期
生命周期:
- 产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
- 死亡:在嵌套的内部函数成为垃圾对象时就死亡了(=null)
垃圾回收
垃圾回收程序必须跟踪记录哪个变量还会使用,以及哪个变量不会再使用,以便回收 内存。在浏览器的发展史上,用到过两种主要的标记策略:标记清理和引用计数。
4.DOM事件流和事件冒泡
事件流
事件委托 (子级的事件委托给父级)
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
<script>
//不用再给每一个li都绑定事件
let oul=document.querySelector('ul')
oul.οnclick=function(e){
oul.removeChild(e.target)//e.target变为事件委托最后一个事件
}
</script>
4.==和===的区别
==会进行类型的转换之后再判断两者是否相等,而===不会进行数据类型的转换,先判断两边的数据类型是否相等,如果数据类型相等的话才会进
行接下来的判断,再进行等式两边值得判断,可以理解为只有等式两边是全等(数据类型相同,值相同)的时候结果才会是true,否则全为false。
5.js输出语句
①alert()
②confirm()和conslo.log()
confirm() 与 alert() 相似,在浏览器窗口弹出一个提示框,使用 confirm() 函数创建的提示框中,除了包含一个“确定”按钮外,还有一个“取消”按钮。如果点击“确定”按钮,那么 confirm() 函数会返回一个布尔值 true,如果点击“取消”按钮,那么 confirm() 函数会返回一个布尔值 false。
③document.write()
document.write() 可以向 HTML 文档中写入 HTML 或者 JavaScript 代码
④innerHTML
设置或者获取指定 HTML 标签中的内容
6.条件,循环语句
相信如果你是计算机专业的,这个章节不用多说什么,就复习一下就好了.
条件语句:if...else swtich..case
循环语句 while do..while(在循环的尾部检查表达式的值,循环会至少执行一次) for
跳出:
break:结束最近的一次循环,可以在循环和switch语句中使用。
continue:结束本次循环,执行下一次循环,只能在循环中使用。
性能优化
但是我们需要去注意语句的运用,比如一个例子(这也是面试官问过我类似的问题)
这样看确实没有问题,那我们加入时间,和放大数值看看
修改一下函数:
对于质数我们应该还发现一个规律,比如36 1 36,2 13,3 12,4 9,6 6 ,9 4......几乎从36开方数6之后就开始重复了,所以如果从36开方数还没有找到因数就可以结束了.
最后一点,偶数肯定不是质数,可以把 2 单独领出来
9.函数(函数也是对象)
函数创建格式:
function 函数名(形参1,形参2,...,形参N) {
语句...return ....
}
fun();//无参
fun(实参1,实参2..)// 有参,多余的形参为undefined
1)匿名函数:
匿名函数:没有名字的函数就是匿名函数,它可以让一个变量来接收,也就是用 “函数表达式” 方式创建和接收。
const fun = function (形参1,形参2,...,形参N) {
.....return ....
}fun();
箭头函数 (ES6新特性)
const fun=(形参1,形参2,...,形参N)=>{
......
return ....
}
//只有一个参数
const test1=(x)=>{return x}
const test2=x=>{return x}
//多个形参 只有一条语句 不需要返回值
let sum = (a, b) => a + b;
2)面试题来了!
普通sa函数和箭头函数有什么区别?
①箭头函数没有自己的this
箭头函数不会创建自己的this对象,只会继承在自己作用域的上一层this。
②箭头函数继承来的this指向永远不会改变
依旧是处于全局执行环境中。
③call()、apply()、bind()等方法不能改变箭头函数中this的指向
箭头函数不能作为构造函数使用
由于箭头函数时没有自己的this,且this指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。
④箭头函数没有自己的arguments
箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。
什么是arguments?
arguments 是一个 对应于 传递函数的参数 的 类数组(array-like)对象
。
⑤ 箭头函数没有prototype
3)立即执行函数
函数定义完,立即被调用,这种函数叫做立即执行函数,立即执行函数往往只会执行一次。
(function (形参1,形参2,...,形参N) {
.......
})();
4)构造函数
新对象通过使用 new 操作符后跟一个构造函数(constructor) 来创建。构造函数就是用来创建新对象的函数,比如下面这行代码:
let now = new Date();
这行代码创建了引用类型 Date 的一个新实例,并将它保存在变量 now 中。Date()在这里就是构 造函数,它负责创建一个只有默认属性和方法的简单对象。ECMAScript 提供了很多像 Date 这样的原生引用类型,帮助开发者实现常见的任务
Date()
Date.parse()和 Date.UTC(),Date.now()
-
Date.parse()
: 解析一个日期字符串并返回相应的时间戳(自1970年1月1日起的毫秒数)。常用于将日期字符串转换为时间戳以便进行日期计算。 -
Date.UTC()
: 返回指定日期和时间的时间戳(以UTC时间为基准)。用于创建一个日期的UTC时间戳,特别是在处理跨时区的数据时很有用。 -
Date.now()
: 返回当前时间的时间戳(自1970年1月1日起的毫秒数)。常用于计算代码执行时间、生成唯一标识符或记录时间戳
剩下的了解一下:
这里刚好来一个moment时间例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入 moment.js -->
<script src="./node_modules/moment/moment.js"></script>
</head>
<body>
<button class="begin">开始会议</button>
<button class="end">结束会议</button>
<script>
let bgeinDate;
let endDate;
let bg = document.querySelector('.begin');
let ed = document.querySelector('.end');
bg.addEventListener('click', () => {
bgeinDate = moment();
console.log(`开始时间: ${bgeinDate.format('HH:mm:ss')}`);
});
ed.addEventListener('click', () => {
endDate = moment();
console.log(`结束时间: ${endDate.format('HH:mm:ss')}`);
if (bgeinDate) {
let duration = moment.duration(endDate.diff(bgeinDate));
let minutes = duration.minutes();
let seconds = duration.seconds();
console.log(`总共耗时: ${minutes} 分 ${seconds} 秒`);
} else {
console.log('尚未设置开始时间');
}
});
</script>
Math()
①min()和 max()方法
②舍入方法:
Math.ceil()方法始终向上舍入为最接近的整数。(往大)
Math.floor()方法始终向下舍入为最接近的整数。(往小)
Math.round()方法执行四舍五入。
Math.fround()方法返回数值最接近的单精度(32 位)浮点值表示
random()
Math.random()方法返回一个 0~1 范围内的随机数,其中包含 0 但不包含 1。
原型:number = Math.floor(Math.random() * total_number_of_choices + first_possible_value)
这里使用了 Math.floor()方法,因为 Math.random()始终返回小数,即便乘以一个数再加上一 个数也是小数。因此,如果想从 1~10 范围内随机选择一个数,代码就是这样的:
let num = Math.floor(Math.random() * 10 + 1);
很多时候,通过函数来算出可选总数和最小可能的值可能更方便, 比如:
function selectFrom(lowerValue, upperValue) {
et choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue); }
let num = selectFrom(2,10);
console.log(num); // 2~10 范围内的值,其中包含 2 和 10
使用这个函数,从一个数组中随机选择一个元素就很容易,比如:
let colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"];
let color = colors[selectFrom(0, colors.length-1)];
在这个例子中,传给 selecFrom()的第二个参数是数组长度减 1,即数组最大的索引值。 注意 Math.random()方法在这里出于演示目的是没有问题的。如果是为了加密而需要 生成随机数(传给生成器的输入需要较高的不确定性),那么建议使用 window.crypto. getRandomValues()。
其他方法:
10.作用域
函数作用域:调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
11.call()、apply()和bind()方法
三这都是functiob的方法,作用都是 改变this指向。
call()
我们先看一下正常情况下:
使用call() 第一个参数为传递的对象,第二个参数为需要传递的参数
传递多个参数:
apply()
bind()
bind()和前两者区别在,前两者是直接调用,而后者是作为返回值返回一个函数,传参方法和call一样.
11.WebStorage(5GB)
WebStorage是HTML5中本地存储的解决方案之一,在HTML5的WebStorage概念引入之前除去IE User Data、Flash Cookie、Google Gears等看名字就不靠谱的解决方案,浏览器兼容的本地存储方案只有使用Cookie。有同学可能会问,既然有了Cookie本地存储,为什么还要引入WebStorage的概念?那就要说一说Cookie的缺陷了:
数据大小:作为存储容器,Cookie的大小限制在4KB左右这是非常坑爹的,尤其对于现在复杂的业务逻辑需求,4KB的容量除了存储一些配置字段还简单单值信息,对于绝大部分开发者来说真的不知指望什么了。
安全性问题:由于在HTTP请求中的Cookie是明文传递的(HTTPS不是),带来的安全性问题还是很大的。
网络负担:我们知道Cookie会被附加在每个HTTP请求中,在HttpRequest和HttpResponse的header中都是要被传输的,所以无形中增加了一些不必要的流量损失。
虽然WebStorage是HTML5新增的本地存储解决方案之一,但并不是为了取代Cookie而制定的标准,Cookie作为HTTP协议的一部分用来处理客户端和服务器通信是不可或缺的,session正是依赖于实现的客户端状态保持。WebStorage的意图在于解决本来不应该Cookie做,却不得不用Cookie的本地存储的应用场景。
WebStorage又分为两种: sessionStorage 和localStorage ,即这两个是Storage的一个实例。从字面意思就可以很清楚的看出来,sessionStorage将数据保存在session中,浏览器关闭也就没了;而localStorage则一直将数据保存在客户端本地; 不管是sessionStorage,还是localStorage,使用的API都相同。
localStorage
在本地永久性存储数据,除非显式将其删除或清空。
常见方法:
保存单个数据:localStorage.setItem(key,value);
读取单个数据:localStorage.getItem(key);
删除单个数据:localStorage.removeItem(key);
删除所有数据:localStorage.clear();
取得给定数值位置的名称:localStorage.key(index);
sessionStorage
sessionStorage对象存储特定于某个对话的数据,也就是它的生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。存储在sessionStorage中的数据可以跨越页面刷新而存在,同时如果浏览器支持,浏览器崩溃并重启之后依然可以使用(注意:Firefox和Weblit都支持,IE则不行)。
因为sessionStorage对象绑定于某个服务器会话,所以当文件在本地运行的时候是不可用的。存储在sessionStorage中的数据只能由最初给对象存储数据的页面访问到,所以对多页面应用有限制。
不同浏览器写入数据方法略有不同。Firefox和Webkit实现了同步写入,所以添加到存储空间中的数据是立刻被提交的。而IE的实现则是异步写入数据,所以在设置数据和将数据实际写入磁盘之间可能有一些延迟。
常见方法:
保存单个数据:sessionStorage.setItem(key,value);
读取单个数据:sessionStorage.getItem(key);
删除单个数据:sessionStorage.removeItem(key);
删除所有数据:sessionStorage.clear();
取得给定数值位置的名称:sessionStorage.key(index);
12.Cookie(4KB)
11.DOM(Document Object Model)文档节点
DOM是一个应用编程接口(API)
- 文档节点:整个HTML文档
- 元素节点:HTML文档中的HTML标签
- 属性节点:元素的属性
- 文本节点:HTML标签中的文本内容
DOM文档操作
查找 HTML 元素
document.querySelector('.box') 和document.getElementById('box1')获取的是节点对象
document.getElementsByTagName('div')和document.getElementsByClassName('box')获取的是
节点对象数组
获取 HTML 的值 和改变 HTML 的值和修改HTML 的值
innerHTML指的是从对象的起始位置到终止位置的全部内容,包括Html标签。
innerText 指的是从起始位置到终止位置的内容,但它去除Html标签。
查询父子:
拓展知识1:
通过style属性设置和读取的都是内联样式,无法读取样式表中的样式或者说正在应用的样式,如果想要读取当前正在应用的样式属性我们可以使用元素.currentStyle.样式名来获取元素的当前显示的样式,它可以用来读取当前元素正在显示的样式,如果当前元素没有设置该样式,则获取它的默认值,但是currentStyle只有IE浏览器支持,其它的浏览器都不支持,在其它浏览器中可以使用getComputedStyle()这个方法来获取元素当前的样式,这个方法是window的方法,可以直接使用,但是需要两个参数:
第一个参数:要获取样式的元素
第二个参数:可以传递一个伪元素,一般都传null
该方法会返回一个对象,对象中封装了当前元素对应的样式,可以通过 对象.样式名 来读取样式,如果获取的样式没有设置,则会获取到真实的值,而不是默认值,比如:没有设置width,它不会获取到auto,而是一个长度,但是该方法不支持IE8及以下的浏览器。通过currentStyle和getComputedStyle()读取到的样式都是只读的,不能修改,如果要修改必须通过style属性,因此,我们可以写一个适配各个浏览器的读取元素样式的方法。
11.防抖和节流(面试题必备)
防抖(debounce):用户触发操作的时间过于频繁,需要实现一段时间内连续触发我就不执行,一段时间后执行(只执行最后一次)
- 实现方式:每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法
- 缺点:如果事件在规定的时间间隔内被不断的触发,则调用方法会被不断的延迟
- 一般使用场景,表单验证,输入框实时搜索
<body>
<div class="box">
<input type="text" placeholder="搜索网页" id="search">
<div id="content"></div>
</div>
<script>
//模拟输入框输入数据,然后后端一段时间再响应(后台console模拟)
const ct=document.getElementById('content')
const sh=document.getElementById('search')
let timer=null;
sh.addEventListener('keyup', function () {
if(timer!==null){
clearTimeout(timer)
}
timer=setTimeout(()=>{
console.log(sh.value)
ct.innerText = sh.value;
},500)
})
</script>
节流(throttle)
每次触发事件时,如果当前有等待执行的延时函数,则直接return
一般使用场景:轮播图,滚动条
<body>
<script>
let flag = true
window.onscroll = function () {
if (flag) {
setTimeout(() => {
console.log('滚动了')
}, 500);
}
flag=false
}
</script>
防抖和节流的区别:如果在节流的地方使用防抖,只能拿到最后一次,就比如滑动滚动条,你一直不停了滑动,只有松开你才结束计数器拿到数据.
11.promise和async await(ES6)
详细知识点可以看一下我这篇:
超级无敌大结合(vue3+scss+axios(ajax+promise))_vue3 scss-CSDN博客
同步和异步
(看代码就理解了)
同步程序执行后再执行异步
单线程
因为js是单线程一个执行完后才会执行另一个,所以计时器是不准的
事件循环
事件顺序:同步,nextTick,异步,setImmediate(第一次事件循环结束后执行)
宏任务微任务
宏任务:计时器,ajax,读取文件
微任务:promise.then
事件顺序:同步,nextTIck,微任务,宏任务,setImmediate(第一次事件循环结束后执行)
async就不用再new Promise了
async,await可以理解为简易般promise
官网的解释:async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。
11.AJAX
传统的web交互是用户触发一个http请求服务器,然后服务器收到之后,在做出响应到用户,并且返回一个新的页面,每当服务器处理客户端提交的请求时,客户都只能空闲等待,并且哪怕只是一次很小的交互、只需从服务器端得到很简单的一个数据,都要返回一个完整的HTML页,而用户每次都要浪费时间和带宽去重新读取整个页面。这个做法浪费了许多带宽,由于每次应用的交互都需要向服务器发送请求,应用的响应时间就依赖于服务器的响应时间,这导致了用户界面的响应比本地应用慢得多。
AJAX 的出现,刚好解决了传统方法的缺陷,AJAX 是一种用于创建快速动态网页的技术,通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新,这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
可以 利用express框架学习:
创建vscode后的操作
npm init --yes
npm i express
AJAX 的核心是 XMLHttpRequest 对象。 所有现代浏览器都支持 XMLHttpRequest 对象。
XMLHttpRequest 对象用于幕后同服务器交换数据,这意味着可以更新网页的部分,而不需要重新加载整个页面。
const xhr=new XMLHttpRequest();
//打开 1
xhr.open('get','http://127.0.0.1:8000/')
//发送 2
xhr.send()
//on 当...的时候 on change当改变的时候
//readystate 0 1 2 3 4
//0表示未初始化 1表示open方法调用完毕 2表示send方法调用完毕
//3表示服务端返回部分结果 4表示返回全部结果
xhr.onreadystatechange=()=>{
if(xhr.readyState===4){
//响应状态码2xx都表示成功
if(xhr.status>=200&&xhr.status<300){
//处理结果 行 头 体(计网报文的知识)
console.log(xhr.status)
console.log(xhr.statusText)
console.log(xhr.getAllResponseHeaders())
console.log(xhr.response)
}else{
}
}
}
XMLHttpRequest对象方法
XMLHttpRequest对象属性
12.DOM文档事件
窗口事件(window)
表单事件(Form)
键盘事件
鼠标事件
媒体事件
其它事件
阻止事件冒泡: event.stopPropagation();
<script>
let test=document.querySelector('.box')
let test1=document.querySelector('.box1')
test.addEventListener('click',function(){
console.log('触发box')
})
test1.addEventListener('click',function(){
console.log('触发box1')
event.stopPropagation();
})
Exception
当错误发生时,JavaScript 提供了错误信息的内置 error 对象。
error 对象提供两个有用的属性:name
和 message
。
error 的 name 属性可返回六个不同的值:
name.value
RangeError
是一个值超过有效范围时,会抛出RangeError范围错误
ReferenceError
变量/项的引用被破坏或不存在时,会抛出ReferenceError范围错误
栈内存超过同样范围出错
TypeError
表示值的类型非预期类型时发生的错误,或者定义的错误等..
看到要知道什么问题:Uncaught (in promise) TypeError: Assignment to constant variable.
未捕获的类型错误:赋值给常量变量。
URIError
编码或解码URI时出现问题时,将引发URIError。
SyntaxError
发生语法错误,出现这个err大家就多去找找语法问题,哪里漏了什么
try catch finally
try {
// 可能发生异常的代码
} catch (error) {
// 发生错误执行的代码
} finally {
// 无论是否出错都会执行的代码
}
try 中一旦出现错误则其它语句不能执行,如果不出现错误则 catch 中的语句不会执行,finally 中的语句无论是否出错都会执行.
throw
使用 throw 关键字抛出来主动抛出异常。
- 每一个错误类型都可以传入一个参数,表示实际的错误信息。
- 我们可以在适当的时候抛出任何我们想抛出的异常类型。
throw new SyntaxError("语法错误...");
果要自定义错误类型,只需要继承任何一个自定义错误类型都可以,一般直接继承Error即可。
15.esc6新特性
①模板字符串
- 字符串中可以出现换行符
- 可以使用 ${xxx} 形式输出变量
②let const
③Set和Map
参考前面的数组方法
④浅拷贝和深拷贝
先复习基础:对象都是存在堆内存的
这个问题在实际项目中就是,如果提交的表单信息改变,所以指向相同对象的都会改变
所以我们实际需要用到拷贝
浅拷贝
但是浅拷贝有个问题就是只能拷贝原始类型,对象类型拷贝不到
function copy(obj){
let newObj={}
for(let i in obj){
newObj[i]=obj[i]
}
return newObj
}
深拷贝
利用递归实现(相当于就是对无法浅拷贝的对象再次浅拷贝)
递归方法
<script>
// let test1={name:'henry'}
// let test2={}//test2变为一个新的对象,就防止都指向一个对象
// test2.name='test'
// console.log(test1.name)
// console.log(test2.name)
let test1 = {
name: 'henry',
phone: '123',
age: '20',
job: {
name: 'web fronted'
}
}
function copy(obj) {
let newObj = {}//变为一个新的对象,就防止都指向一个对象
for (let i in obj) {
if (obj[i] instanceof Object) {
newObj[i] = copy(obj[i])
} else {
newObj[i] = obj[i]
}
}
return newObj
}
let test2 = copy(test1)
test2.name = 'test'
test2.phone = 'test'
test2.age = 'test'
test2.job.name = 'test'
console.log(test1)
console.log(test2)
</script>
JSON.stringify()和JSON.parse()
JSON是js的数据格式哈,一般就是涉及到前后端交互,这个方法会把对象转为字符串
另外一个方法 JSON.parse()可以又把它转回对象,这个过程中实现了独立,转换后的对象不再是原来的那个对象
<script>
let test1 = {
name: 'henry',
phone: '123',
age: '20',
job: {
name: 'web fronted'
}
}
function copy(obj){
let tostr=JSON.stringify(obj);
let newobj=JSON.parse(tostr)
return newobj
}
let test2=copy(test1)
test2.name = 'test'
test2.phone = 'test'
test2.age = 'test'
test2.job.name = 'test'
console.log(test1)
console.log(test2)
</script>
至此,也诞生出了一张经典的表:
⑤箭头函数
⑥rest参数
rest参数(形式为"…变量名"),用于获取函数的多余参数,这样就不需要使用arguments(参数)对象了.
参考文章:
学习JavaScript这一篇就够了-CSDN博客
JavaScript中的数组方法总结+详解_js+数组+unshift-CSDN博客
书籍:js高级程序设计(第4版)
视频:b站晓舟报告(非常建议大家看)