三. JavaScript

690 阅读27分钟

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.b34. 也就是说 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. 
        如果是letconst 定义的变量, 则该变量不允许访问即不能获取也不能修改,直到定义这个变量那个语句执行之后,才可以访问. (也就是说letconst 定义的变量也有变量提升,只不过说不允许访问, 如果访问就会报错.暂时性死区)
    (3) 将实参值和形参 统一
    (4) 找函数声明, 将函数名作为AO的属性名,将函数体作为值.
3 解释执行

3.4 函数作用域

function a() {
	function b() {}
}
test.[[scope]] 这里边存的是函数的域,其中存储了执行期上下文的集合

作用域链 * 作用域链2

  • 闭包: 内部的函数被保存到外部,必须生成闭包, 闭包会导致作用域链不释放.
  • 闭包的作用: 公有变量累加器,可以做缓存,可以实现封装 属性私有化,模块化开发,防止污染全局变量

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) nullundefined不能调用.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']
因为ab 指向了同一块内存空间

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)把下标24的元素,复制到下标为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)  { }
不存在变量提升
暂时性死区: 在一个代码中使用letconst生命变量,在变量执行到该声明语句之前,变量是不可以访问的.我们称之为暂时性死区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() {}
  • 箭头函数
* () => {}
* thisarguments 指向上层作用域的thisarguments
* 箭头函数没有显示原型prototype, foo.prototypeundefined, 不能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   ???
在Es11Promise.allSettled 跟原来ES6Promise.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: {value11writablefalseenumerablefalseconfigurablefalse},
    name: {value'h'writabletrueenumerabletrueconfigurabletrue}
}

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_INTEFER9007199254740991

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 获取节点的引用

节点类型nodeNamenodeTypenodeValue
标签节点标签名称1null
属性节点属性名称2属性值
文本节点#text3文本内容不包括HTML代码
注释节点#comment8注释内容
  • 获取子节点:
    • 标签节点.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()|阻止冒泡|
  • 事件委托

事件委托.png

  • 坐标获取
    ev.clientX, ev.clientY   相对于浏览器可视窗口坐标
    ev.pageX, ev.pageY    相对于页面坐标
    ev.screenX, ev.screenY  相对于电脑屏幕坐标
    ev.offsetX, ev.offsetY   相对于带有定位的父盒子的x,y坐标
    ev.button   0左键    1滚轮键    2右键

xy.png

  • 浏览器本身:
介绍: 一个页面的展示, 从外到内的容器为 屏幕 浏览器 以及页面本身. 
           HTML元素展示在页面内, 页面展现在浏览器内 而浏览器展现在屏幕内
           通过js的一些对象可以获取这些容器的高度 宽度
      物理尺寸和分辨率:  容器的尺寸是指当前分辨率下的高度 宽度, 而不是物理高度 宽度
                         如22寸的显示器, 屏幕分辨率为1366*768, 那么获取到的屏幕
			高度为1366px, 宽为768px.