1.JS介绍
1.1 编程语言
-
编程: 就是让计算机解决某些问题, 使用程序设计语言编写程序代码,并最终得到结果.
-
计算机程序: 是机算计所执行指令的集合.
-
计算机语言: 指的是人与计算机之间通讯的语言,它是人与计算机之间传递信息的媒介.
-
计算机语言的分类: 机器语言, 汇编语言 和高级语言
-
CPU最终执行的都是机器语言, 它是由0和1组成的二进制数. 二进制是计算机语言的基础.
-
汇编语言: 和机器语言实质相同, 它是直接对硬件操作的, 只不过指令采用了英文缩写的标识符,容易识别和记忆.
-
高级语言: 是相对于低级语言而言, 它并不是特指某一种具体的语言,而是包括了很多编程语言,常用的有C语言, C++, Java, C#, Python, PHP, JavaScript, Go语言, 等等...
-
编程语言和标记语言的区别:
编程语言: 有很强的逻辑和行为能力. 在编程语言里会看到if else, for, 等具有逻辑性和行为能力的指令,这是主动给CPU发指令.
标记语言: (html)不用于向计算机发指令, 经常用于格式化和链接. 标记语言的存在是用来被读取的, 他是被动的.
1.2 JS历史
- JavaScript首次出现在1996年, 它最初的设计目标是改善网页的用户体验----Brendan Eich
- LiveScript, 后来与Sun公司合作将其改名成JavaScript
1.3 浏览器组成
- 一: shell---外壳部分
- 二: 内核 ----- (渲染引擎, JS引擎, 其他模块)
哪些内核:
Chrome blink
firefox Gecko
Safari webkit
IE trident
Opera presto---->blink
1.4 解释型语言和编译型语言
编译型: 通篇翻译成一个新的文件, 统一执行, 优点执行速度快, 多线程 C, C++
解释型: 解释一行执行一行, 优点跨平台, 单线程 Python, JavaScript
java: .java通过javac编译成.class---->用jvm 解释一行执一行
ECMA标准:
(1)微软推出了JScript
CEnvi推出了ScriptEase,
它俩与JavaScript同样在浏览器上运行. 为了统一规格JavaScript兼容了ECMA标准,因此也称为ECMAScript.
(2)ECMAScript规定了,js的编程语法和基础核心知识,是所有浏览器厂商共同遵守的一套JS语法工业标准
(3)JavaScript三部分组成: ECMAScript (JavaScript语法), Dom(页面文档对象模型) Bom(浏览器对象模型)
1.3 JavaScript引入方式
(1)
<script type="text/javascript">
js代码...
.....
</script>
(2)
<script type="text/javascript" src="xxx.js"> </script>
2.基础语法
2.1 变量
变量声明和赋值 var const let
var a;
a =100;
var b = 300;
var a = 10, b = 23, c = 34;
- 变量命名规范,数字字母下划线和$符组成,非数字开头, 不能是关键字和保留字
- 用es6语法的时候, 不要写var了,尽量用let和const. let是定义变量,const是常量. let可以看成是更完美的var
- 块级作用域
- JS中使用var来声明一个变量时,变量的作用域主要是和函数的定义有关
- 针对于其他的块来说是没有作用域的, 比如if/for等等,这在我们开发中往往会引起一些问题.
- const 定义为常量,不能被修改. const定义时必须同时赋值
- const 定义引用类型的常量时,不能更改其指向但是里边的数据可以更改. 例如: const obj = {name: 'lisi',age:18}, 不能用obj = new Object(), 但是可以obj.name = 'wangwu';
- 对象字面量增强写法:
- const obj = new Object()
- 增强写法为: const obj = { }
- 属性的增强写法: key与value相同时,可以简写. const obj = { name , age}
- 函数的增强写法: getName(){}
2.2 原始值和引用值 (两种数据类型)
- 不可变数据类型 原始值,存储在stack栈中 六种: Number,Boolean,String,undefined,null,symbol
- 引用值存在堆中: array Object function ..date Regexp
栈内存: (不可变数据类型,原始值,贴牌方式 )
(1) var a=3; 把变量a指向存放数据3的内存址
(2) var b = a; 把a的指向赋值给b, b也指向3的内存地址
(3) var c=3 把变量c也指向3的内存地址
(4) a,b,c指向的内存地址相同, 栈内存数据不能重复. 给不同变量赋相同的值,它们指向的内存空间是一样的.
(5)不可变数据类型修改值, a,b,c的指向会发生改变. 修改a,b,c的值实际就是修改了a,b,c的指向
堆内存: (可变数据类型,引用值, 贴牌方式)
(1) var m = [3,4]; 把变量m指向存放数据[3,4]的内存址
(2) var n = m; 把m的指向赋值给n,n也指向了这个数组
(3) var p= [3,4]; p指向另外一个[3,4]的内存地址
(4) m,n指向同一个地址, p指向另外一个地址, 堆内存数据可以重复.给不同变量赋与 值一样的数组,它们指向的内存空间不一样.
(5)可变数据类型修改值, m,n,p指向的内存地址 不会发生改变
(6) var x = [2,3,5,[22,23,34],7]
赋值: var y = x 既不是浅拷贝也不是深拷贝
浅拷贝: 通过浅拷贝得到y = [2,3,5,[22,23,34],7] 该数组是一个全新的地址,但是内层数组还是指向原来的地址,如果x的内层数组发生变化,y的内层也会发生变化, 因为他们指向相同的地址
深拷贝: 与浅拷贝的不同之处是 层的数组等引用类型,也是完全重新拷贝的.
2.3 js的语法错误
- 一般在语句结束加;
- 一个html文件可以放多可script标签包裹的代码块. js语法错误会引起后续代码终止, 但不会影响其它js代码块
- js逻辑错误会影响错误后边的代码执行, 不会影响代码前边的代码执行
2.4 js运算符
(1)算术运算符
+ - * / % = () 赋值号优先级最弱, ()优先级最高, 0/0返回NaN, 1/0返回Infinity
++ -- += -= /= *= %=
--a, a--
(2)比较运算符
< > <= >= == !=
Infinity == Infinity 返回true
NaN == NaN 返回false
返回布尔值, 字符串比的是ASCII值
(3) 逻辑运算符
&& || ! 先把符号两端的表达式 转成布尔值,
&& 返回结果是最后一个表达式的值或者是第一个为false表达式的值
|| 返回结果是最后一个表达式的值或者是第一个为true表达式的值
! 先将表达式变布尔值,然后取反
注: undefined null NaN "" 0 转为布尔值是false 其它是true
window.prompt('input) 输入框
2.5 条件
- if, else if,else
- 把if后()括号里的表达式 返回的东西 转成布尔值
- switch case
- break
- contune
2.6 循环
-
for(var i=0; i<10; i++) { 语句... }
-
for(var i=0; i<10; i+=3) { 语句... }
-
for(var i=0; i<10;) { 语句...; i++ }
-
var i=0; for(; i<10;) { 语句...; i++ }
-
var i=0; for(; i--;) { 语句...;}
-
for in
-
for of
-
while() {}
-
do{} while()
2.7 数据类型
- typeof返回7种 number, string, boolean, undefined, symbol. object,function
- 数字类型number:包括
- typeof(null)返回object
2.8 显示类型转换
(1)
Number()转换成数
false --> 0
true --> 1
null --> 0
undefined --> NaN 数字类型的非数
'a' --> NaN
'-123' --> -123
'123asdkjf' --> NaN
(2)
parseInt(data,进制radix)转换成整数
radix的取值范围是2---36或0
false --> NaN
true --> NaN
null --> NaN
undefined --> NaN 数字类型的非数
'a' --> NaN
'-123.9' --> -123
'123asdkjf' --> 123 砍断原则
(3)
parseFloat() 转换成小数, 没有radix进制参数
(4)
String() 转成字符串, 写的所有内容都转成字符串
(5)
.toString(radix) 转成字符串
undefined和null 不能用toString方法
123.toString(16) 当成10进制转成16进制
(6)
Boolean()转换成布尔
undefined null NaN "" 0 转为布尔值是false 其它是true
2.9 隐示类型转换
(1)
isNaN() 判断是否是NaN 实质内部先调用Number()
NaN --> true, 是NaN
123 --> false
'123' --> false
'asds' --> true, 是NaN
'324sdf' --> true, 是NaN
null --> false
undefined --> true, 是NaN
(2)
++,--,正负号+ -, - * / %, 实质内部先调用Number()
'123'++ --> 124
'sdf'++ --> NaN
'324sdf'++ --> NaN
null++ --> 1
undefined --> NaN
(3)
&& || ! 实质内部先调用Boolean()
(4)
> < >= <=, == != 实质内部先调用Number()
比较大小的有数字'b'> 3 就先调用Number()转成数字
两个都是字符串 'a' > 'd' 就比较ASCII码值
(5) 补充:
加号+ 两端表达式 默认是转换成Number(),但是如果两端有字符串默认调用toString
=== !== 不发生类型转换,, 值与类型都相等才返回true
NaN == NaN 返回false
NaN === NaN 返回false
typeof()返回值是以字符串形式呈现
3 函数
3.1 基础
-
函数声明: function ttt(a,b){}
-
函数表达式:
- var getNum = function abc() {} 命名函数表达式
- var setNum = function() {} 匿名函数表达式
-
函数参数:可以形参多,也可以实参多
- 在每一个函数里边都有一个 类似数组 arguments, arguments拿到的是实参列表
- 如果形参列表长,那么没有接收到参数的形参值是 undefined
- 函数名.length代表形参长度, arguments.length代表实参长度
- a = 3; arguments[0] = 5; console.log(a);的值是5
- arguments内部变 形参a,b的值也会跟着变,是因为他们有内部的映射规则, 它们实际是指向两个不同的内存空间. 如果没给形参传值,就不映射了
-
函数返回值, 若不写,隐式的执行return. return终止函数并返回值
3.2 递归
3.3 js运行三部曲
1 语法分析: 通篇扫描一篇, 但是不执行
(1) 任何变量未声明就赋值,此变量就为全局变量所有, a=10; 打印window.a值是10.
(2) 一切声明的全局变量,都是window的属性 var b = 34; 打印window.b 是34. 也就是说 window是全局的域
function ttt() { var a = b = 35; } 先把35的内存地址赋给b, 再把b的地址给a, 由于b是未用var声明的, 所以b是全局变量, a 是局部变量.
(3) if 语句体里不允许定义function
2 预编译
(1) 预编译创建AO对象 Activation Object(执行期上下文,作用域)
(2) 将形参和变量声明 作为AO的属性名, 值为undefined.
如果是let 和const 定义的变量, 则该变量不允许访问即不能获取也不能修改,直到定义这个变量那个语句执行之后,才可以访问. (也就是说let 和 const 定义的变量也有变量提升,只不过说不允许访问, 如果访问就会报错.暂时性死区)
(3) 将实参值和形参 统一
(4) 找函数声明, 将函数名作为AO的属性名,将函数体作为值.
3 解释执行
3.4 函数作用域
function a() {
function b() {}
}
test.[[scope]] 这里边存的是函数的域,其中存储了执行期上下文的集合
*
- 闭包: 内部的函数被保存到外部,必须生成闭包, 闭包会导致作用域链不释放.
- 闭包的作用: 公有变量累加器,可以做缓存,可以实现封装 属性私有化,模块化开发,防止污染全局变量
3.5 立即执行函数
第一种:
(function (){ var c = a+b }) (a,b) 针对初始化功能的函数
第二种:
(function (){} (a,b))
* 只有表达式才能被执行符号执行, 执行符号是括号(),
* 函数声明不是表达式 不能被执行,例如:
function (a,b){ var c = a+b }() 这个会报错,不能被执行.
function (a,b){ var c = a+b }(3,4); 这个不会报错,也不能被执行. 系统会把他当成两个语句.
*
第三种:
表达式可以执行 例如 var m = function (a,b){ var c = a+b }(3,4) 可以被执行,执行完毕立即销毁
第四种:
+ function ttt(){ var c = a+b }(a,b) 表达式能被执行,ttt执行完立即销毁,打印ttt是undefined
+ - ! && 可以实现上边第四种立即执行函数
4.字符串
4.1
5.对象
5.1 基础 var aa = {}
- 增: aa.new_val = value 来增加
- 删: delete this.aa
- 改: aa.new_val = value2 修改
- 查: aa.new_val
- 一个变量没声明使用,会报错. 一个属性没声明就使用返回undefined
5.2 创建对象
1 对象的创建方法
(1) var obj = {} 对象字面量,plainObject
(2) 系统自带的构造函数 Object()
var obj = new Object()
(3) 自定义的构造函数 function Person() {}
2 构造函数的内部原理 (三段式)
(1)在函数体最前面隐式的加上this={}
(2)执行this.xxx = xxx;
(3) 隐式的return this,, 如果显示的自己写一个return {};
那么所有用Person创建出来的对象 如var p1 = new Person() 的打印结果都是空对象{}, 如果自己返回的的原始值return 34; 只要是原始值系统就会过滤掉,不会返回34,会强制的返回this
3 初识包装类
原始值不能有属性和方法,不能被更改.
原始值设置属性会变成 引用值 隐式的调用new Number(4).len=5; 然后delete 销毁.
undefined不能设置属性
null不能设置属性
5.3 原型
- 定义: 原型是function对象的一个属性, 它定义了构造函数 制造出的对象的公共祖先. 通过该构造函数定义出来的, 可以拥有该原型的属性和方法.
Person.prototype = {
height: 233, // 类属性
width: 34, // 类属性
foo: funtion(){}, // 类方法
constructor: function Person() {}, // 构造这个对象的构造函数
__pro__: , // 指向的是父类继承过来的的属性和方法
}
(1) function Person() {} 其中Person是函数引用.
函数也是一个对象,是对象就有属性和方法.
系统自带的属性Person.prototype 就是原型. 这个 prototype 是一个对象{} .
(2) 这个prototype里边装的是: 类属性,类方法 和 constructor 以及__pro__
实例化出来的对象可以访问 类属性和类方法 console.log(p1.height) 也可以 prototype.height来访问
而p1.width = 45那么类属性里的width不会改变. 是在实例属性中重新定义了一个width的属性.
用 delete p1.width可以删除实例属性,不能删除类属性. 用delete prototype.width可以删除类属性.
(3) 通过p1.constructor 构造这个对象的构造函数. 就是说Person.prototype这个对象里边默认有一个类属性叫constructor,它的值是这个构造函数的表达式
(4) 这个__pro__ 指向的是父类继承过来的的属性和方法.
(5) 当new出来p1的时候默认执行 var this = {__proto__ : Person.prototype}; 这个Person.prototype里边也有它自己的__proto__.
(6) Object.prototype是所有的对象的原型
(7) var obj = {}; 用对象字面量定义对象, 系统隐式调用了 new Object()
var obj2 = new Object()
var ttt = { name: 'vha', age: 18 };
var obj = Object.create(原型);方法来创建对象
(8) null和undefined不能调用.toString(), 因为它们没有原型.
var num = 345; num.toString(); 可以调用
345.toString() 不能调用, 因为345. 首先被计算机识别成浮点型数据
(9) toString()被不同的地方重写了,如Object,Number,Array,Boolean,String. 当不同的类创建出来的对象调用toString()方法,会调用自己的. [多太]
5.4 call/apply
- test()执行 相当于test.call()执行
- Person.call(obj, name,30); 相当于在function Person() { 默认执行了 this=obj} ,
从而覆盖了里边的this={ __proto__: Person.prototype} - call() 的根本作用是改变 构造函数内部默认的this指向
- 父类名.call(this, a, b)
- apply与call相同, 不同之处是 父类名.apply(this, [a, b])
5.5 this ???
5.6 继承的 几种方式
(1) 原型链继承
(2) 构造函数继承
(3) 组合继承
(4) 寄生组合继承
(5) class 类继承
6.JavaScript面向对象
6.1面向对象的介绍
- 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的一次调用就可以了.
- 面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作. (继承封装多态)
- 对象是特指的事务,对象是由属性和方法组成的
6.2 class
(1)
class name {
constructor(name) {
this.name = name
}
sing(song) {
console.log("我唱歌:" + song)
}
}
(2) 构造函数
constructor()方法是类的构造函数 默认方法,用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法. 如果没有显示定义, 类内部会自动给我们创建一个constructor(),
constructor()函数接收传递过来的参数,同时返回实例对象.
(3) 继承
class Father { // 父类
constructor(x,y) {
this.x = x
this.y = y
}
sum() {
console.log(this.x + this.y)
}
money() {}
say() {
console.log("我是爸爸")
}
}
class Son extends Father {
constructor(x, y, m) {
super(x,y) // super() 必须放在this的前面
this.m = m
}
say() {
super.say() // 子类中调用父类的普通方法
console.log("这是儿子")
}
}
let son1 = new Son()
son1.sum(3,4)
son1.say()
constructor() 里面的this指向实例对象, 普通方法里面的this指向这个方法的调用者
6.3 构造函数和原型
- class是Es6之后才新增的, 在Es6之前,使用构造函数和原型来模拟类的一个实现机制
(1)
创建对象可以通过三种方式:
* 对象字面量
* new Object()
* 自定义构造函数
function Star(name, age) {
this.name = name
this.age =age
this.sing = function () {
console.log("我会唱歌")
}
}
let ldh = new Star("刘",3)
(2)
在Es5的时候,构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用. 我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面.
new 在执行时, 3步曲:
* 分配内存
* 在内存中创建一个空对象, 让this指向这个对象. 即 this = {}
* 执行构造函数里面的代码, 为实例化出来的对象赋初始值, 并且返回这个新的对象.
(3) ???
7.数组
7.1 定义数组
-
var a = new Array("she","hello",2,3,4)
-
var a = ['k','y',8,8,9]
-
var a = new Array(3) 这是定义了一个长度为3的 空数组
-
var a = [10.4] 不报错
-
var a= new Array(10.2)会报错
-
arr[20] = 'tom' 数组直接抻长到21位---->溢出读是undefined, 可以溢出写
-
数组的长度 a.length
-
var a = Array.of(3,7,9) 打印结果为[3,7,9]
-
var a = Array.of(3) 这是定义了一个长度为1的数组 [3]
-
new Array(3).fill("Ok"); 这是新建了一个长度为3,并且有默认值的数组
-
[5,6,7,8,9].fill("你好",1,4) 其中1,4为下标,包括1不包括4
7.2 数组的引用
var a = [6,8,'k']
var b = a
b[0] = 3
console.log(a) 的结果为 [3,8,'k']
因为a 与 b 指向了同一块内存空间
7.3 数组常用方法
(1)可以改变原数组的方法
push()末尾追加元素,
pop() 末尾删除元素
unshift() 头部追加
shift() 头部删除 var d = arr.shift() 其中d接收的是被移除的元素
sort() 排序
reverse() 逆序
清空数组3中方法
a = []
a.length = 0
a.splice(0)或者 a.splice(0,a.length)
copyWithin()
var arr = [3,4,5,6,7,12,13,14,15]
arr.copyWithin(6,2,4)把下标2到4的元素,复制到下标为6的位置
打印arr: [3,4,5,6,7,12,5,6,15]
splice(下标,个数)
var arr = [7,8,9,10,11,12,33]
var b = arr.splice(2,4) 从下标是2的位置,向后截取4个. 第二个参数代表总共几个.
打印 b 结果为 [9,10,11,12]
打印 arr 结果为 [7,33]
var c = arr.splice(2,4,'你好','ok')
打印 c 结果为 [9,10,11,12]
打印 arr 结果为 [7,'你好','ok',33]
var d = arr.splice(2,0,'深圳')
打印 d 结果为 []
打印 arr 结果为 [7,'深圳',8,9,10,11,12,33]
(2)不可以改变原数组的方法
Array.isArray(a) 来判断传过来的是否数组
concat
arr = [2,3], brr=[4,9]
var c = arr.concat(brr)
打印 arr 的结果为 [2,3]
打印 c 的结果为[2,3,4,9]
切片slice(下标,下标)
* var arr = [6,7,8,9,10,11]
var b = arr.slice(1,3) 该切片会产生一个新的数组, 不会改变原数组arr
打印 b ,结果为[7,8], 包括左不包括右
* arr.slice() 不传参数,则会全部截取. 得到一个和原来完全相同的数组
* arr.slice(2) 表示从下角标为2的位置一直截到最后
* arr.slice(-3) 从倒数第三的位置截取到末尾
indexOf和includes
arr.indexOf(15) 通过元素查下标值, 查找不到返回-1
arr.lastIndexOf(15) 从后向前找
arr.indexOf(15,3) 从下标为3的位置向后查找
arr.indexOf(15,-4) 从倒数第四的位置向后查找
arr.includes(15) 通过元素查下标值,有true,没找到返回false
arr.includes 当arr中的元素是对象引用类型时,includes查找不到. 此时还是用find
find和findIndex
arr.find( item => console.log(item) ) 这个find会自动遍历arr这个数组
find返回的是具体的值,findIndex返回的是具体的索引位置
(3)数组 转 字符串
[3,4,'h'].toString() 可以将数组转成字符串, 不会改变原来数组
String( [3,4,7] ) 这种方式也可以转成字符串
[2,3,5].join("=") 可以用一个字符将数组连接成 字符串, 不会改变原数组
toLocaleString() 返回一个字符串表示数组中的元素。数组中的元素将使用各自的toLocaleString方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开
(4)字符串 转 数组
"hello,world".split(",")拆分成数组,得到['hello','world']
Array.from(str) 将一个字符串拆成一个数组, 每个(字符)是数组的一个元素
Array.from(obj)可以转成一个数组,其中obj必须有length这一项
(5) 类数组arguments, DOM
(6) 数组去重
7.4 展开语法
var a = ['k','m']
var b = [3,4,5]
var c = [...a, ...b] 得到 ['k','m',3,4,5]
...a 展开语法,相当于 'k','m' ...b相当于3,4,5
例:
function sum(...args){
console.log(args)
}
sum(2,2,5)
这个打印结果为[2,2,5], 相当于把2,2,5 赋值给args这一个变量args
7.5 解构语法
(1)
var arr = ['hello',10]
var m = arr[0]
var n = arr[1]
用解构赋值: var [m, n] = arr. 这时console.log(m)的值是一样的, 打印n也是一样的
(2) var [...arr] = "hello" 相当于 var arr = ['h','e','l','l','o']
(3) var [ , name] = ["军人"," 张三"] 把'张三'这个值赋给了name
(4) var [name, ...args] = ['李四','小刘', 22] 把'李四'赋给name,把'小刘',22 值赋给了args
打印args是数组 ['小刘',22]
(5) var [name, age='18'] = ["韩信"] name的值为'韩信' age有默认值
7.6 遍历数组
- for (let i=0; i<arr.length; i++){ console.log(i) }
- for (let value of arr) { console.log(value) } 打印数组中,每一个元素的值
- for (let index in arr) { console.log(index) } 打印数组中每一个元素的下标
- forEach()
arr.forEach( (item) => console.log(item) )
里边的箭头函数还可以传更多的参数
arr.forEach( (item,index,arr_old) => { console.log(item,index,arr_old, this) }, {} )
其中item是数组中的每项元素
index接收每项元素的下标
arr_old是 原数组arr
this打印的是这个对象 { }, 如果forEach的第二个参数不传,则this打印window
7.7 迭代器
(1) keys
let arr = ["h","e" ,"t"]
let keys = arr.keys()
let {value, done} = keys.next()
console.log(value, done) 其中value打印的是索引,done打印是否迭代完成
(2) values
let arr = ["h","e" ,"t"]
let values = arr.values()
let {value, done} = values.next()
console.log(value, done) 其中的value打印的是值,done打印是否迭代完成
(3)entries
let arr = ["h","e" ,"t"]
let en = arr.entries()
let {value, done} = en.next()
console.log(value, done) 其中的value打印的是一个数组,数组第一项为下标,第二项为值,done打印是否迭代完成
相当于前两种方法的结合体
(4)every
let arr = [89, 67, 45, 99, 100, 70]
let status = arr.every( (item,index,arr_old) => {
console.log(item,index,arr_old)
return item > 60
} )
此时全部元素都大于60, status的值才为true, arr_old是原数组arr
(5) some
let arr = [89, 67, 45, 99, 100, 70]
let status = arr.some( (item,index,arr_old) => {
console.log(item,index,arr_old)
return item == 100
} )
此时只要有元素等于100, status的值就为true
(6)过滤器
let arr = [89, 67, 45, 99, 100, 70]
let status = arr.filter( (item,index,arr_old) => {
console.log(item,index,arr_old)
return item > 80
} )
此时status是一个新数组,只要大于80就在status这个数组中
7.8 数组映射
(1)
let arr = [3,4,5,'k','a']
let t = arr.map( (item,index,arr_old) => return item + 'OK' )
打印t, 为每一项末尾都加上了'OK'
打印arr,并未改变. 但是arr中的元素要是引用类型的数据,则arr中元素会被改变
(2)
let arr = [1,2,3,4,5,6,7]
let t = arr.reduce( (pre,item,index,arr_old) => {
console.log(pre,item)
return pre + item
} )
第一次调用的时候pre是1, item是2
第二次调用的时候pre是上次调用的返回值,value是3
(3)
let arr = [1,2,3,4,5,6,7]
let t = arr.reduce( (pre,item,index,arr_old) => {
console.log(pre,item)
return pre + item
}, 55 )
第一次调用的时候pre是55, item
第二次调用的时候pre是上次调用的返回值,item是2
......
8.正则
8.1 简介
- regular expression: RegExp(处理字符串的一种规则)
- 用来处理字符串的一种规则, 只能处理字符串,不是字符串不能处理,但是可以使用toString()方法变为字符串
8.2 创建正则的方式
- 创建正则的方式有两种
字面量: let reg1 = /\d+/
构造函数: let reg2 = new RegExp('\\d+')
8.3 正则表达式组成
(1) 表示数量的 元字符 /^a$/
* 零到多个 /^a*$/
+ 一到多个 /^a+$/
? 零到一个 /^a?$/
{n} n个 /^a{n}$/
{n,} n到多个 /^a{n,}$/
{n,m} n到m个 /^a{n,m}$/
(2) 特殊元字符 表示有哪些内容 /^a$/
\ 转义
. 除换行符之外的任意字符
^ 以什么什么开头
$ 以什么什么结尾
\d 0-9之间的一个数字
\D 除了数字都可以匹配
\w 数字,字母,下划线中的任意一个字符
\n 换行符
\t 一个制表符(一个TAB键,四个空格)
\s 一个空白字符串(包含空格, 制表符,换行符)
\b 匹配一个单词的边界
x|y x或y中的一个字符
[xyz] xyz中的一个字符
[^xyz] 除了x y 和z 以外的任意一个字符
[a-z] a到z这个范围的任意一个字符
[A-Z] A到Z这个范围的任意一个字符
[^a-z] 非a到z这个范围的任意一个字符
() 正则中的分组符号
(?:) 只匹配不捕获
(?=) 正向预查
(?<='对话会议') 后寻断言
(?!) 反向预查
(3) 修饰符
放在正则表达式外面 /[a-z]{5,}/igs
i(ignoreCase) 忽略大小写
m(multiline) 可以进行多行匹配
g(global) 全局匹配
u(Unicode) 用来正确处理大于\uFFF的Unicode字符
y(sticky) 黏连
s(dotAll) 让 . 能匹配任意字符, 包含\n\r
(4)
srt.match(reg)
reg.exec(str1)
9. Date 和 Math 和 try catch 和 节流防抖
9.1 Date
- 它是处理日期和时间的
- 使用date用 new实例化相应对象
(1) 属性
(2) 方法
9.2 Math
- math是一个内置对象, 它具有数学常数和函数的属性和方法.不是一个函数.
- 不是构造函数,所以我们不需要用new来调用, 直接使用里边的属性和方法即可
(1)属性
(2)方法
9.3 try catch
try{
console.log('a')
console.log('b')
}
catch(e) {
console.log(e.massage + e.name)
}
错误类型 e.name 一共6种:
EvalError: eval()的使用与定义不一致
RangeError: 数值越界
ReferenceError: 非法或不能识别的引用
SyntaxError: 发生语法解析错误
TypeErrot: 操作数类型错误
URIError: URI处理函数使用不当
9.4 防抖debounce
- 就是指连续触发事件但是在设定的时间段内 只执行最后的一次.
- 如设定300毫秒触发, 多次触发重置这个时间.
let timerId = null
function debounce() {
if(timerId !== null) {
clearTimeout(timerId)
}
timerId = setTimeout(() => {
console.log("我是防抖")
// 执行真正要执行的操作
}, 300)
}
* 应用: 搜索框搜索输入
9.5 节流 throttle
- 就是指连续触发事件但是在设定的一段时间内, 只执行一次函数.
let timerId = null
function throttle() {
if(timerId !== null) {
return
}
timerId = setTimeout(() => {
console.log("我是节流")
// 执行真正要执行的操作
this.timerId = null
}, 300)
}
* 应用: 高频事件: 如 scroll事件, 快速点击, 鼠标滑动, resize
下来加载 和 视频播放记录时间等等
10.宏任务 微任务 和 严格模式
10.1 事件循环
(1)
js是单线程的, 也就是说,同一个时间只能做一件事. 所以它会阻塞代码, 为了防止阻塞代码,我们把任务分成同步和异步.
(2)
同步代码: 立即放入JS引擎执行, 就是在执行栈中执行
异步代码: 先放入宿主环境(浏览器/node)执行,因为宿主环境是多线程的如浏览器是基于C++的, 并不阻塞主线程继续往下执行,异步结果在将来执行.
(3) 执行栈执行完毕,会去任务队列看 是否有异步任务, 有就送到执行栈执行.
反复循环查看执行,这个过程就是事件循环
10.2 宏任务 微任务
(1) 同步任务放在 执行栈里执行
(2) Promise.then 放在 微任务队列里执行 (优先执行微任务队列, 与宏任务队列相比.)
(3) settimeOut 放在 宏任务队列里执行
在宏执行任务队列时,里进行新一轮的循环 <同步任务, 宏任务,微任务>
await 下边所有的代码都是微任务,异步的
(4) 微任务包括(JS引擎执行的)
process.nextTick()
Promise.then() catch()
Async/Await
Object.observe 等等
(5) 宏任务(宿主环境执行的, 比如浏览器或者node等等)
script 代码块
setTimeout/setInterval
setImmediate定时器
10.3 Es5的严格模式
'use strict' 放在最顶行, 这是全局使用. 也可以放在函数内部,也是第一行,这是局部使用
es5 不允许使用arguments.callee func.caller with
eval('') 能将字符串当代码执行
11.
12.ES6---ES12 新特性
12.1 Es6新特性
- 变量声明
let
变量不能重复声明
块级作用域 (全局, 函数, eval) { }
不存在变量提升
暂时性死区: 在一个代码中使用let和const生命变量,在变量执行到该声明语句之前,变量是不可以访问的.我们称之为暂时性死区temporal dead zone( TDZ )
const
常量声明时必须同时赋值
常量值后不能修改
一般用大写命名
符合块级作用域
对数组和对象里边的元素的修改, 不算 对常量的修改
- 解构赋值
* const arr1 = ['1','2','3','4']
* let [aa, bb, cc] = arr1
* let [aa, ...arr9] = arr1 后边的元素放到新的数组中
* let [aa , bb = 3] = arr1 带默认值的
* let { mm, nn } = obj
- 模板字符串
声明: 两个``
内容中可以直接出现换行符
变量的拼接 ${a}
function getFoo(m, n) { console.log('hello') }
getFoo`` 这个函数会被执行
getFoo`world` 会把m当做参数传递给m
- 对象字面量增强
对象简化
{
name: name,
age: age,
gender: gender,
}
{
name,
age,
gender
}
方法声明简化
myMethod() {}
- 箭头函数
* () => {}
* this和arguments 指向上层作用域的this和arguments
* 箭头函数没有显示原型prototype, foo.prototype是undefined, 不能new foo() 不能作为构造函数实例化对象
* 可简写 res => aaa
- 函数参数默认值
* 具有默认值的参数, 位置一般放在最后
* 有默认值的函数的length属性
function foo(x,y,z) {}
console.log(foo.length) 是3
function foo(x,y,z=9) {}
console.log(foo.length) 是2
function foo(x, y, z=9, m, n) {}
console.log(foo.length) 是2
function foo(x, y, m, n, z=9) {}
console.log(foo.length) 是4
- rest剩余参数
* ES6中引用了rest parameter, 可以将不定数量的参数放入到一个数组中, 如果最后 一个参数是 ...为前缀的, 那么它将会将剩余的参数放到该参数中, 并且作为一个数组;
function myMethod(x,y,z, ...arg) { console.log(arg)}
myMethod(1,2,3,4,5,6,7,8,9)
* rest剩余参数必须放在最后
* 剩余参数rest 和 arguments的区别
(1)剩余参数只包含那些没有对应形参的实参, 而arguments对象包含了传给函数的所有实参
(2)arguments对象不是一个真正的数组, 而rest参数是一个真正的数组,可以进行数组的所有操作.
(3)arguments是早起的ECMAScript中为了方便去获取所有的参数提供的一个数据结构,而rest参数是ES6中提供,并且希望以此来替代arguments的
- 展开语法
let a= [1,2,3]
let arr = [6,7,8, ...a,89]
const names = ["aaa", "ccc", "fff"]
const str = "Tom"
const info = {name: 'jack', age: 88}
(1) 函数调用时
function foo(x,y,z) {
console.log(x,y,z)
}
foo.apply(null, names), 通过apply的方式第二个参数接收一个数组, 会把数组的每一项挨个赋值给x, y, z
foo(...names) 可以使用展开语法, 把names数组中的每一个值分别赋值给x,y,z
foo(...str) 是把"Tom"这个字符串展开, 把每个字符分别赋值给x,y,z
(2) 构造一个数组时
const newArr = [...names, ...str]
(3) ES9中, 构建对象
const obj = {...info, address: "shenzhen"}
(4) 补充: 展开运算符其实进行的是一个浅拷贝
const info = {
name: 'jack',
friend: {name: kobe}
}
const obj = {...info, name: "tom"}
obj.friend.name = "zhang"
console.log(info.friend.name) 也会改变
(5)
concat 不改变原来数组
var c = [...a,...b]
var m = [...a] 数组克隆
- 进制的写法
二进制 const num = 0b1010
八进制 const num = 0o3456
十六进制 const num = 0x33ac23
Es2021中 大数 const num = 10_000_000_000
- Symbol 一种基本数据类型
ES6引入的一种新的数据类型,表示独一无二的值. 它是JavaScript语言的第七种基本数据类型,是一种类似于字符串的数据类型.
(1) Symbol特点
Symbol值是唯一的,用来解决命名冲突的问题
Symbol值不能与其他数据进行运算
Symbol定义的对象属性不能使用 for in 循环遍历, 可以使用Reflect.ownKeys来获取对象的所有键名.
(2)创建Symbol
let s = Symbol()
let s2 = Symbol('hello')
let s3 = Symbol('hello')
s2 == s3 返回false
let s4 = Symbol.for('world')
let s5 = Symbol.for('world')
s4 == s5 返回true
(3) ES10中, Symbol还有一个描述
const s11 = Symbol("aaa")
console.log(s3.description) 是aaa
(4) Symbol值作为key
s12 = Symbol()
s13 = Symbol()
s14 = Symbol('kkk')
s15 = Symbol('jjj')
const obj = {
[s12]: "fff",
[s13]: "asdf",
}
obj[s14] = "tom"
Object.defineProperty(obj, s15, {
enumerable: true,
configurable: true,
writable: true,
value: "zhang"
})
获取: obj[s12], obj[s13]
不能用点语法来获取
Object.keys(obj)也是获取不到的
Object.getOwnPropertyNames(obj)也是获取不到的
Object.getOwnPropertySymbols(obj) 这个可以获取到
(5)
const s18 = Symbol.for("aaa")
const s19 = Symbol.for("aaa")
console.log(s18 === s19) 是true
const key = Symbol.keyFor(s18)
- 生成器(一个特殊函数)???
* 生成器函数是ES6提供的一种异步编程解决方案, 语法行为与传统函数完全不同
* function * gen() { }
* 生成器函数参数
- promise 异步编程的一种解决方案 ???
new Promise(参数), 参数本书就是一个函数
new Promise( (resolve, reject) => { resolve() 一旦你在这个位置调用resolve,他就会在最后边调用一个.then()})
promise三种状态:
首先当我们开发中有异步操作时, 就可以给异步操作包装一个Promise
pending: 等待状态,正在进行网络请求或者定时器没到时间等等
fulfill: 满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
reject: 拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
.then(函数1,函数2) 函数2是当reject时,自动执行函数2
.then(函数1).catch(函数2) 这样也可以执行函数2
then中 可以继续返回一个resolve
(1)
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('aaa')
}, 1000)
}).then(res => {
// 1.这里有100行自己的业务代码
// 2.对结果进行处理
return new Promise((resolve, reject) =>{
resolve(res + "567")
})
})
(2)
上边的第2步可以简写成 return Promise.resolve(res + '567')
也可以简写成 return res + '567'
链式调用过程中, 各个.then中出现了异常,都会在.catch中捕获到, 手动抛出的异常throw也能在catch中捕获到
(3)
Promise.all([网络请求1, 网络请求2]).then(results => {
results是一个数组,, 里边包含了第一个请求的结果,, 第二个请求的结果
})
在Es9中 新增Promise finally ???
在Es11中 Promise.allSettled 跟原来ES6的 Promise.all()不同 ???
- Set
是一个新增的数据结构, 可以用来保存数据, 类似于数组,但是和数组的区别在于 元素不能重复
(1)
const set1 = new Set()
set1.add(10)
set1.add(10)
set1.add(33)
console.log(set1) 打印 {10, 33}
set1.add({})
set1.add({})
console.log(set1) 打印 { 10, 33,{},{} }
(2)
const arr = [3,4,5,6,7,45,34,3,4]
const set2 = new Set(arr)
arr2 = [...set2] 或者 arr2 = Array.from(set2)
console.log(arr2) 实现数组去重的效果
(3) 常见的属性和方法
const arr = [3,4,5,6,7,45,34,3,4]
const set3 = new Set(arr)
set3.size 元素个数
set3.add(99) 添加
set3.delete(45) 删除
set3.has(3) 是否包含
set3.clear() 清空
set3.forEach() 遍历
for(const item of set3) {} 遍历
- WeakSet
(1)
内部的元素也是不能重复的
内部只能存放对象类型, 不能存放基本数据类型
对元素的引用是弱引用, 如果没有其他引用 对某个对象进行引用, 那么GC可以对该对象进行收回
let obj = {
name : "tom",
friend: {
name: "jack",
}
}
const set1 = new WeakSet()
set1.add(obj)
obj = null
(2)WeakSet常见的方法
set1.add(10) 添加
set1.delete (10) 删除
set1.has(10) 是否包含
注: 因为WeakSet是弱引用, 如果我们遍历获取到其中的元素,那么有可能造成对象不能正常的销毁
所以存储到WeadSet中的对象是没办法一个一个获取的.
(3) WeadSet应用场景
const set2 = new WeakSet()
class Person {
constructor() {
set2.add(this)
}
running() {
if(!set2.has(this)) throw new Error("不能通过其他对象调用running方法")
console.log("running", this)
}
}
const p = new Person()
p.running()
p.running.call({name:"why"})
p = null
- Map
一种新增的数据结构,用于存储映射关系的
用对象存储{} key只能是字符串,如果你写的不是字符串最终也会被转成字符串
const obj1 = {name: "kobe"}
const obj2 = {name: "tom"}
(1)
const info = {
[obj1]: "aaa",
[obj2]: "bbb"
}
console.log(info) 打印 { '[object Object]' : 'bbb'}
(2)map 允许我们用对象类型来作为key
const map1 = new Map()
map1.set(obj1, 'aaa')
map1.set(obj2, "bbb")
console.log(map1) 打印 { {name:'kobe'}: 'aaa', {name:'tom'}: 'bbb' }
const map2 = new Map([]) 可以传一个数组,但是必须是entrins的数组
(3) 常见方法
map1.size 个数
map1.set 添加或修改
map1.get 获取
map1.has("key1") 是否包含
map1.delete("key1") 删除
map1.clear() 清空
map1.forEach() 遍历
for of 遍历
map1.clear()
- WeakMap
Map的key可以是对象,也可以是字符串, WeakMap的key只能是对象
WeakMap的key对象的引用是弱引用, 如果没有其他引用 指向这个对象, 那么GC可以回收该对象.
(1)
let obj2 = {name: "tom"}
let map2 = new WeakMap()
map2.set(obj2, "aaa")
obj2 = null
(2)常见方法
没有size 属性
不能进行遍历
map2.set(obj, 'ttt') 添加或修改
map2.get(obj) 获取
map2.has(obj) 添加或修改
map2.delete(obj) 删除
(3)应用场景
const obj3 = {
name: "tom",
age: 18
}
function obj3Fn1() {}
function obj3Fn2() {}
const weakMap = new WeakMap()
const obj3Map = new Map()
obj3Map.set("name333",[obj3Fn1, obj3Fn2])
weakMap.set(obj3, obj3Map)
监听变化
obj3.name = "james"
const targetMap = weakMap.get(obj3)
const fns = targetMap.get("name333")
fns.forEach(item => item())
12.2 ES7新特性
- Array.prototype.includes
const arr1 = ["abc", "aaa","bbb","ccc"]
arr1.includes("bbb") 返回true
arr1.includes("bbb", 3) 返回 false
之前用arr1.indexOf(NaN) 没办法判断NaN的,
arr1.includes("NaN") 是可以判断NaN的
- 幂运算 2 ** 10, 等价于Math.pow(2, 10)
- 装饰器 ???
12.3 ES8新特性
-
async和await 是promise和生成器的结合 ???
-
Object.values 获取所有的value值, 放在一个数组中
const obj = { name: "tom", age: 17 }
console.log( Object.keys(obj) ), 打印 ['name', 'age']
console.log( Object.values(obj) ), 打印 ['tom', '17']
console.log( Object.values(['aaa', 'bbb', 'ccc']) ), 打印 ['aaa', 'bbb', 'ccc']
console.log( Object.values("the") ), 打印 ['t', 'h', 'e']
- Object.entries
const obj = { name: "tom", age: 17 }
console.log(Object.entries(obj)) 打印 [ ['name','tom'], ['age',17] ]
console.log(Object.entries(['aa','bb','dd']))
打印[ ['0','aa'], ['1','bb'], ['2', 'dd'] ]
console.log(Object.entries("the"))打印[ ['0','t'], ['1','h'], ['2', 'e'] ]
- String Padding
某些字符串我们需要对其进行前后的填充,来实现某种格式化效果,ES8中增加了padStart和padEnd方法,分别是对字符串的首尾进行填充.
const message = "HelloWorld"
const newMsg = message.padStart(15) 前边用空格填充,使字符串长度达到15
const newMsg = message.padStart(15, "*") 前边用星号填充,使字符串长度达到15
- Object.getOwnPropertyDescriptors 获取到一个对象的所有的属性描述符
const obj = {
name: 'h'
}
Object.defineProperty(obj, 'age', {
value: 11
})
const result = Object.getOwnPropertyDescriptors(obj)
console.log(result)
{
age: {value: 11, writable: false, enumerable: false, configurable: false},
name: {value: 'h', writable: true, enumerable: true, configurable: true}
}
12.4 ES9新特性
- Async iterators 迭代器 ???
* ES6创造了一种新的遍历命令for...of循环
* 原生具备iterator接口的数据:
* Array
* Arguments
* Set
* Map
* String
* TypedArray
* NodeList
- 正则扩展--> 命名捕获分组???
- 正则扩展--> 反向断言???
- 正则扩展 --> dotAll模式???
12.5 ES10的新特性
- Object.fromEntries
const obj = {
name: "why",
age: 18,
height: 1.68
}
const arr = ['tom','jack','colin']
const entries1 = Object.entries(obj)
[["name", "why"], ["age",18], ["height",1.68]]
const entries2 = Object.entries(arr)
[["0", "tom"], ["1","jack"], ["2","colin"]]
fromEntries的使用:
const newObj = Object.fromEntries(entries1)
const qStr = 'name=why&age=3&height=2.25'
const qParams = new URLSearchParams(qStr)
console.log(qParams) 打印{ 'name' => 'why','age' => '3','height' => '2.25' }
for (const param of qParams) {
console.log(param) 打印 ["name", "why"] ["age", 3] ["height", "2.25"]
}
const param2 = Object.fromEntries(qParams)
console.log(param2) 打印 {"name", "why","age", 3,"height", "2.25"}
- trimStart() 和 trimEnd() 清除字符串左/右空格
const msg = " hello world "
msg.trim() 是去除左右的空格
msg.trimStart() 是去除左的空格
msg.trimEnd() 是去除右的空格
- flat() 和 flatMap(), 将多维数组变成一维数组
(1)
flat() 就是数组里边多了这么一个方法, 让数组降维
const nums = [10, 20, [2,4], [[3,6],[4,5], 9], 78, [55,66]]
const newNums = nums.flat() 一次降维, 深度为一
const newNums2 = nums.flat(2) 二次降维, 深度为二
(2)
flatMap()方法首先使用映射函数映射每个元素,然后将结果压缩成一个新的数组
注意一: flatMap是先进行map操作, 在做flat的操作
注意二: flatMap中的flat相当于深度为1
const nums2 = [10,20, 30]
const newNums2 = nums2.flatMap(item => {
return itme * 2
}, this) this 可以不传
应用场景:
const msg = ['hello world', 'tom jack', 'my name is yrvh']
let arr = []
for (const m of msg) {
const msg2 = m.split(" ")
arr = [...arr, ...msg2]
}
const arr2 = msg.flatMap(item => {
console.log(item)
return item.split(" ")
})
- Symbol.prototype.description
- Optional catch binding: ???
12.6 ES11的新特性
- String.prototype.matchAll ???
- 动态导入 Dynamic Import ???
- import meta ???
import("../").then()
- 大整数BigInt, 基本数据类型
在ES11之前, 最大安全数字 const maxInt = Number.MAX_SAFE_INTEFER 为9007199254740991
const bigInt = 900719925474099133n
console.log(bigInt + 10) 会报错
console.log(bigInt + 10n) 加法运算
const num = 100
bigInt + BigInt(num)
大数转成普通数 Number( bigInt )
- 空值合并操作 Nullish Coalescing Operator
let foo
console.log(foo) undefined
const bar = foo || "default value"
const bar2 = foo ?? "default value"
- 可选链操作符 ?. 的组合
const info = {
name: "why",
friend: {
name: "tom",
girlFriend: {name: "hmm"}
}
}
console.log(info.friend?.girlFriend?.name)
- 全局对象 globalThis
在之前我们希望获取JavaScript环境的全局对象, 不同的环境获取的方式是不一样的
在浏览器中可以通过this,window来获取
在node中我们需要通过global来获取
globalThis适合各个环境下, 来指向全局对象
- for in 进行标准化
for in 遍历出来的item 都指向key
12.7 ES12的新特性
- FinalizationRegistry 对象可以让你在对象被垃圾回收时请求一个回调
const finalRegistry = new FinalizationRegistry((value) => {
// 对象被销毁的时候执行
console.log(value + "被销毁") 打印: obj_key被销毁
})
let obj = {name: "why"}
finalRegistry.register(obj, "obj_key")
obj = null
注: let tempobj = obj 如果有另外一个对象依然指向 obj所指的那个对象, 将obj赋予别的值 改其指向 obj = null, 那么这个对象依然不会被销毁, 因为这个对象是一个强引用, 它还有tempobj在指向它.
改成弱引用:
(1)
let tempobj = new WeakSet()
tempobj.add(obj)
(2)
ES12中新加了个 WeakRef,
let tempobj = new WeakRef(obj) 这一个弱引用
tempobj.deref() 就是原来obj 所指向的那个对象
- 逻辑赋值运算
let msg = undefined
msg = msg || "default value"
简写成 msg ||= "default value"
||= 逻辑或
&&= 逻辑与
??= 逻辑空
- NumericSeparator 10_000_000_000
- String.replaceAll 字符串替换
12.8 ES13的新特性
- at方法
let arr = ['aa','ss','dd']
arr.at(1)
aar.at(-1)
let str = 'hello tom'
str.at(1)
str.at(-1)
- Object.hasOwn
const obj = {
name: 'tom',
age: 18,
}
obj.hasOwnProperty("name")
Object.hasOwn(obj, "name") 新出的方法用来替代 Object.prototype.hasOwnProperty
区别:之前是用实例对象来调用的, 而hasOwn使用类名直接调用的.
const info = Object.create(null) 如果直接把原型执行null
info.name = "tom"
console.log(info.hasOwnProperty("name")) 会报错, 因为原型是null 没有hasOwnProperty这个方法
用Object.hasOwn(info, "name")这种方式 就是可以的
- class类型加了新的成员字段
public private static
class Person {
school = "xiwangxiaoxue" // 公共属性
#address = "bj" // 私有属性, 在class内部才可以访问
static gj = "china" // 静态属性
static #tt = "the"
static { // 静态代码块
}
constructor(name, age) {
this.name = name
this.age = age
}
}
const p = new Person('zhang',3)
13 深拷贝,浅拷贝,等号赋值
(1) 浅拷贝
function shallowClone(obj) {
const newObj = {}
// 循环拿到对象的属性名(key值)
for(let prop in obj) {
// 只检查自己身上的属性名,不检查原型链上的属性名
if(obj.hasOwnProperty(prop)) {
newObj[prop] = obj[prop]
}
return newObj
}
}
let obj = { name: 'tom', skill: ['js', 'css'] }
let obj2 = shallowClone(obj)
obj2.sill[0] = 'vue'; // 浅拷贝,深层次改变会 影响源数据obj
obj2.name = 'jack'; // 浅拷贝, 浅层次的改变, 不会影响源数据obj
let obj3 = obj; // 直接等号赋值, 不属于深拷贝 也不属于浅拷贝
obj3.name = 'trump' // 等号赋值,obj3和obj指向同一块堆空间, 所以会影响源数据obj
(2) 深拷贝
function deepClone(obj, hash = new WeakMap()) {
if(obj === null) return obj; // 如果是null或者undefined 直接返回
if(obj instanceof Date) return newDate(obj); // 如果是日期类型, new Date之后直接返回, 该数据对象不具备深层次的结构,直接new Date 返回就行.
if(obj instanceof RegExp) return new RegExp(obj); // 正则与日期同理
if (typeof obj !== "object") return obj; // 基本类型和function类型的, 直接返回不需要深拷贝
// ----符合条件并且对象类型的数据 进行深拷贝--------------
if(hash.get(obj)) return hash.get(obj) // 判断集合中是否已经拷贝了数据,防止循环引用
let cloneObj = new obj.constructor() // 找到的是所属类 原型链上的constructor,而原型上的constructor指向的是当前类本身
hash.set(obj, cloneObj);
for(let key in obj) {
if(obj.hasOwnProerty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepCloe(obj[key], hash)
}
}
return cloneObj;
}
let newObj = deepClone(target)
newObj.map.set("key","value")
newObj.set.add("1")
14.session和cookie和token 和 session storage 和 local storage
14.1 为什么要有session和cookie
- 源于web系统的发展和变迁的
- web1.0 强调的是资源的共享
- web2.0 强调的是交互, 交互意味着是有多步操作的,请求和请求之间是有依赖关系的.http是无状态的协议. 所以交引入session和cookie的机制, 来实现状态的记录.
- web3.0 强调的是双赢
Session 和 cookie的特征
- session和cookie都是由服务器生成的,都是用来存储特定的值(键值对)
- session是存储在服务器的,而cookie是会返回给客户端的.一般来说,SessionID会以类似于cookie的方式返回给客户端.
- sessionID是服务器用来识别,操作存储session值的对象的.
- 一般来说,在服务器端,session的存储方式有文件方式,数据库方式,sessionID就是用来识别这个文件的,识别数据库的某一条记录的.
- 客户端在发送请求的时候,会自动将存活,可用的cookie封装在请求头中 然后和网络请求一起发送.
- cookie和session都是有生命周期的
- cookie生命周期受到两个因素的影响,一个是服务器生成时设定的.第二个是客户端是否保留了cookie,客户端是否保留cookie只对客户端自己有影响,对其他封包工具是没 影响的.
- session的生命周期也受到两个因素的影响,服务器对于session对象的保存最大时间的设置.第二 他是和客户端进程绑定,他是写在内存里边的,客户端关闭session会消失.关闭时只对客户端自己有影响.
- cookie和session都是有其作用域的
- cookie有持久化cookie和绘话级cookie
17.BOM (browser object model 浏览器对象模型)
17.1 window为全局对象
- console.log(window.document); 页面文档对象
- console.log(window.frames); 浏览器框架集合
- console.log(window.navigator); 浏览器机器功能信息的对象
- console.log(window.screen); 屏幕信息
- console.log(window.location);
- console.log(window.history); 历史记录
location:
href 控制地址栏
reload 刷新页面, reload(true) 不带缓存刷新
assign() 加载新页面, 在历史记录有痕迹
replace() 加载新页面, 在历史记录不留痕迹
history:
windows.history.length 历史记录的长度
windows.back()上一页
window.forward()下一页
window.go(num) 跳到第10页
window方法:
window.open(“test.html”,”blank”,”width=90px,height=90px,left=9,top=9”)
window.close() 关闭
17.2 window事件
- 加载事件 window.onload=function(){alert(“页面加载成功”)} 只有一个有效,后写的被覆盖,,,当页面完全加载成功出发此页面
- window.onunload 确认关闭
- 滚动事件 window.onscroll 发生滚动时执行 滚动高度var a=document.documentElement.scrollTop||document.body.scrollTop
- 窗口变化事件: window.onresize=function(){w=document.documentElement.clientWidth ||document.body.clientWidth || window.innerWidth, h=............ }
17.3 定时器
- 间歇定时器: vartime time=window.setInterval(function(){console.log(“hello”)},2000) function func1(){window.clearInterval(time)} 清除定时器
- 延时定时器: var time= window.setTimeout(function(){console.log(“world”)},3000)
18.DOM
> 文档对象模型(DocumentObjectModel), 是W3C组织推荐的处理可扩展的标记语言(HTML或者XML) 的标准编辑接口. DOM可以看成树形结构,我们称为DOM树.
文档的概念是: 一个页面就是一个文档,DOM中使用document表示.
18.1
- DOM是访问HTML和操作HTML的标准
- Core DOM 是核心DOM针对任何结构化文档的标准模型
- XML DOM 是针对XML文档的标准模型
- HTML DOM 是针对HTML文档的标准模型
18.2 节点分类
- 文档节点:整个页面html代码是个文档
- 标签节点:也叫元素节点
- 属性节点:不是css 是标签的属性
- 文本节点:网页里边的文字
- 注释节点:注释
18.3 DOM节点层级关系(DOM树)
- 父节点: parent node可以拥有任意数量的子节点
- 子节点: child node
- 兄弟节点: sibling 拥有相同父节点的节点,叫做兄弟节点
- 根节点: root node 一个html文档一般只有一个根节点,它是没父节点的
- 祖先节点: 父节点也是祖先节点
- 后代节点: 子节点也是后代节点
18.4 通过值 获取标签节点的引用
- 通过id: document.getElementById(“idDiv”)
- 通过class: document.getElementsByClassName(“classDiv”)
- 通过name属性: document.getElementsByName(“inputText”)
- 通过div标签: document.getElementsByTagName(“div”), 可以将document换成父元素节点.
- 通过p标签: document.getElementsByTagName(“p”)
- querySelector():返回指定选择器的第一个元素
- querySelectorAll():返回指定选择器的集合
- 获取特殊元素body,
document.body - 获取特殊元素html,
document.documentElement
注意: (1).因为我们文档页面是从上往下加载, 所以先得有标签,我们的script要写到该标签的下边.
(2). 返回的是一个元素对象
(3).console.log() 打印对象只显示[Object object]
console.dir() 打印对象显示 对象完整的数据结构
console.table() 以table表格的形式打印对象
console.info()
18.5 通过标签节点的引用获取和设置 属性值 文本值 css的值
- 获取属性节点的值:
- 官方定义的属性: 标签节点.属性名 直接赋值就是修改
- 自定义的属性: 标签节点.getAttribute(“属性名”) 标签节点.setAttribute(“名”,”值”) 修改属性值,若无则增加
- 移除某个属性: 标签节点.removeAttribute(“属性名”)
- 获取文本节点的值:
- 标签节点.innerHTML 获取开始标签到结束标签 里边的内容(非标准,不保留空格和换行)
- 标签节点.outerHTML 包括开始 结束标签
- 标签节点.innerText (获取开始标签到结束标签 里边内容的文字) 只获取文字(W3C标准, 保留空格和换行)
- 设置css样式的值:
- 标签节点.style.backgroundColor=”#fff”
- 标签节点.style[“height”]=”300px”
- 以上这两种可以给行间样式,内部,外部样式 赋值. 若获取只能获取到行间样式的值
- 通过更改标签的class类型来改变样式
- this.className='change' 来设置class的类名, this.class 是错误的,因为class是js的关键字.
- 获取css样式的值
- window.getComputedStyle(标签引用, 伪类). width
- window.getComputedStyle(标签引用, 伪类)[“height”]
- 以上两种能获取行内样式 内部样式 外部样式, 不能赋值
- 标签节点.offsetTop 是整型的 前边获取的css样式是字符串型的
- 获取和修改表单的属性
- 表单里面的值,文字内容是通过 value来修改的.
- input.value = "xxx"
- btn.disabled = true 或 this.disabled = true
18.6 获取节点的引用
| 节点类型 | nodeName | nodeType | nodeValue |
|---|---|---|---|
| 标签节点 | 标签名称 | 1 | null |
| 属性节点 | 属性名称 | 2 | 属性值 |
| 文本节点 | #text | 3 | 文本内容不包括HTML代码 |
| 注释节点 | #comment | 8 | 注释内容 |
- 获取子节点:
- 标签节点.childNodes
- 第一个子节点: 标签节点.firstChild
- 最后的子节点: 标签节点.lastChild
- 获取子元素节点:
- 标签节点.children(非标准, 获取节点类型为1 的子节点)
- 标签节点.firstElementChild (非标准,不兼容)
- 标签节点.lastElementChild (非标准, 不兼容)
标签节点.children[0] (非标准,兼容)标签节点.children[节点.children.length-1] (非标准,兼容)
- 获取该节点的html文档 根节点:
- 标签节点. ownerDocument
- 获取当前节点的父节点:
- 标签节点.parnetNode
- 获取同级节点:
- 前一个: 标签节点.previousSibling
- 后一个: 标签节点.nextSibling
- 获取同级节点:
- 标签节点.nextElementSibling (有兼容性问题)
- 标签节点.previousElementSibling (有兼容性问题)
- 获取该节点的所有属性节点:
- 标签节点.attributes
18.7 对标签节点的操作
-
创建标签 var a=document.createElement(“p”) (效率偏中1)
-
标签里添加文本 a.innerHTML=”hello” (拼接字符串方式效率低0, 数组方式效率最高2)
-
标签里添加样式 a.style.backgroundColor=”red”
-
var jsDiv = document.gerElementById(“box1”)
-
标签里插入子节点 jsDiv.appendChild(a) 插在最后
-
插入到某某之前 jsDiv.insertBefore(a,jsDiv.lastChild)
-
创建文本节点 var tt=document.createTextNode(“你好”)
-
替换子节点 jsDiv.replaceChild(a, jsDiv.lastChild)
-
复制节点:
- var newDiv1= jsDiv.cloneNode( ) 只复制当前节点
- var newDiv2=jsDiv.cloneNode(true) 复制当前节点和子节点
-
删除节点:
- jsDiv.removeChild(jsDiv.lastChild) 删当前节点的最后一个子节点
- jsDiv.parentNode.removeChild(jsDiv)删除当前节点的兄弟节点
-
返回有定位的祖先节点:
var temp=jsDiv.childNodes[1].offsetParent;- console.log(temp) 返回距离最近的,,如果都没定位,返回body
18.8 事件
- 添加事件的方式
行间添加 <div id=”box1” onclick=”alert(‘你好’)”> </div>
函数添加 <div id=”box2” onclick=”func()”> </div>
function func(){ alert(“你好”)}
标签节点添加<div id=”box3”> </div>
var jsdiv3=document.getElementById(“box3”)
jsdiv3.onclick = function(){ alert(“中”)}
jsdiv3.onclick = function(){ alert(“中国”)}这个事件把前个事件覆盖
jsdiv3.onclick = null 事件就清除了
监听事件添加<div id=”box4”> </div>
var jsdiv4=document.getElementById(“box4”)
function ff(){ alert(“世界”) }
Jsdiv4.addEventListener(“click”,ff ,false)
Jsdiv4.addEventListener(“click”,ff ,false)不会覆盖上一个
jsdiv4.removeEventListener(“click”,ff ,false)
this指向: this大多指向window
但是写在标签里则指向该标签, 在事件函数里指向调用这个函数的那个标签
- 事件种类
聚焦事件focus 离焦事件blur
单击事件click 双击事件dblclick
进入mouseover 离开mouseout 按下mousedown 抬起mouseup 移动mousemove
禁用右键菜单contextmenu 禁止鼠标选中selectstart
键盘按下keydown 键盘抬起keyup (非功能按键才会触发)
Document.addEventListener(“keydown”,function(e){
var event3= e || window.event
console.log(event3) },false)
- 事件流
事件捕获true 事件冒泡false
w3c事件流分为三个阶段: 事件捕获阶段, 处于目标阶段, 事件冒泡阶段
var event3= e|| window.event event3.stopPropagation()阻止冒泡出去
a标签: var info = window.confirm(“您浏览的页面存在风险,是否继续?”)
if (info==false){
event3.preventDefault 返回原页不会继续跳转}
- 事件对象: event,系统自动给我们创建的 |事件对象属性方法|说明| |:-:|:-:| |e.target|返回触发事件的对象| |e.srcElement|返回触发事件的对象,非标准| |e.type|返回事件的类型,如click| |e.cancelBubble|阻止冒泡, 非标准| |e.returnValue|组止默认行为, 非标准| |e.preventDefault()|阻止默认事件| |e.stopPropagation()|阻止冒泡|
- 事件委托
- 坐标获取
ev.clientX, ev.clientY 相对于浏览器可视窗口坐标
ev.pageX, ev.pageY 相对于页面坐标
ev.screenX, ev.screenY 相对于电脑屏幕坐标
ev.offsetX, ev.offsetY 相对于带有定位的父盒子的x,y坐标
ev.button 0左键 1滚轮键 2右键
- 浏览器本身:
介绍: 一个页面的展示, 从外到内的容器为 屏幕 浏览器 以及页面本身.
HTML元素展示在页面内, 页面展现在浏览器内 而浏览器展现在屏幕内
通过js的一些对象可以获取这些容器的高度 宽度
物理尺寸和分辨率: 容器的尺寸是指当前分辨率下的高度 宽度, 而不是物理高度 宽度
如22寸的显示器, 屏幕分辨率为1366*768, 那么获取到的屏幕
高度为1366px, 宽为768px.