1.let和const声明块级作用域
作用域是程序中的变量可见的区域,它决定了你声明变量是在哪里可以使用的,然而在js中只有在函数内部才会创造新的作用域,现在对于变量创建新的作用域可以(1.创建一个函数 2.创建一个代码块,声明let或者const变量它们将只会在这个块中可见,这里区分一下代码块与函数作用域吧if和for循环创建的是代码块)

image.png
在第一个循环中,我们发现即使我们再一次声明let i=5循环并没有结束,也没有报重复声明的错误,而且连续打印了3次5,其实在这里有两个作用域:父级作用域是(let i=0;i<3;i++)和子作用域{let i =5},console.log(i)的时候将会先从自己的作用域中找寻i如果找到了将打印i = 5;如果花括号内是{console.log(i)}将会依次打印0,1,2因为它发现自己的作用域中没有i将会向父作用域(let i=0;i <3;i++)中去寻找;对于函数function a(i){ let i =3}我们发现报了重复声明的错误,函数的理解和for的代码块的理解是不同的,整个函数看作一个作用域,(而不是将a(i)看作父作用域,花括号{}看作子作用域)所以将会报重复声明的错误。if的理解同for,如下图

image.png
这里你也可以想到一个很经典的问题就是点击li弹出数字的解决方法可以有(1.创建立即执行函数造成独立的函数作用域,2.将for循环中的var声明为let创建独立的块级作用域)

image.png

image.png
这里强调1:let和const声明的变量不能在定义前调用,因为它不会像var声明的变量被编译器提升到作用的开始处2:var在全局作用声明的变量是挂在window下面的而let const不是
3:const声明变量时必须要赋值否则会报编译错误
2.关于this与箭头函数中的this
这个真的是很恼火的东西,因为之前已经讲过了(https://www.jianshu.com/p/2d6907cc0760)这里不再重复赘述了,直接讲箭头函数中的this吧,这里你只需要记住并理解一句话就好了:箭头函数中的this是在定义时绑定的,this查找只会向上查找函数作用域,如下图也是很经典的问题

image.png
对于a.show()运行的结果,setTimeout中箭头函数将去查找定义时的this,于是找到show函数的作用域,而show函数的this是在运行时a.show()定义的也就是对象a所以将两次打印结果都是a对象,但是我们换种方式调用的话,如下图

image.png
对于b.show,第一层的this在运行时绑定b,所以打印b对象,而setTimeout中的函数为普通函数调用所以指向window,所以打印window,我们也很容易发现this的指向是搭配函数改变的,要弄清es5中的this指向还是再看一次这篇文章吧(https://www.jianshu.com/p/2d6907cc0760)箭头函数中的this就是那一句话,取决于定义时的this,然后向上查找函数作用域,是函数作用域哦,不是看直属对象哦,比如下图的例子

image.png
3.深入研究“类”
js与java中面向对象语言中的类不同,后者称作面向类的语言更精确一些,在java中,我们创建的类是对象的模板,需要一个新对象的时候,我们实例化这个类,这部操作是将类的方法和属性复制到一个新的实体上,这个实体称作实例,实例是我们自己的对象,且在实例化之后与父类将没有内在关系,而js中没有这样的复制机制,在js中实例化一个类创建一个新对象,但是这个新对象不独立于类,相反它创建了一个与原型相连接的对象,即使在实例化之后对于原型的修改将会传递到实例化的新对象去,也就是说(1.js不存在类似于java面向对象语言中类的概念 2:js中的class很大程度只是原型继承的语法糖)
使用的时候需要注意以下的事情(1:类只能包含方法定义,不能有数据属性 2:定义方法时,需要使用简写方法定义 3:与创建对象不同,不能在类中使用逗号分隔方法定义 4:我们可以在实例化对象上直接引用类的属性)如下图

image.png
类有一个独有的特性,就是constructor构造方法,在构造方法中我们可以初始化对象的属性,构造方法的定义不是必须的,如果不写构造方法,引擎会为我们插入一个空的构造方法,使用extends创建子类会有以下特点
(1.如果你的派生类需要引用它的父类,可以使用super关键字 2.一个派生类不能有一个空的构造函数,即使这个构造函数就是调用了一下super(),你也得把它显示的写出来,但是派生类可以没有构造函数 3.在派生类的构造函数中,必须先调用super,才能使用this关键字,仅在构造函数中this这样使用,在其他地方可以直接使用this)
在js中仅有两个super关键字的使用场景(1.在子类构造函数中调用,如果初始化派生类是需要使用父类的构造函数传递任意参数 2.引用父类的方法如下图)

image.png

image.png
4.深入学习原型
现在我们开始关注calss是怎么映射到js内部原型机制的(使用构造调用创建对象/原型连接的本质/属性和方法委托/使用原型模拟类)
构造函数:使用new关键字调用任意函数会使其返回一个对象,这一步称作创建一个构造调用,这种函数被称为构造器

image.png
当我们使用new关键字调用函数时,js内部执行了下面四个步骤(1.创建一个新对象O 2.将O连接到原型,注意这里是连接并不是拷贝复制,也就是不同于java的地方 3.将函数的this引用指向O,4.隐式返回O)在第三步和第四步之间,引擎会执行你函数中的具体逻辑,现在我们重写Food方法,是指不用new关键字也能工作

image.png
直观原型链。在通常情况下,js中包括函数在内的所有对象都会链接到另一个对象上就是原型,如果我们访问对象本身没有的属性,js就会在对象的原型上检查该属性,也就是如果你向一个对象请求它没有的属性,他会对你说我没有,去我原型上找吧(这个过程叫做委托:在另一个对象上查找不存在的属性过程)

image.png
sara本身没有toString方法没有关系,他会把查找操作委托到原型上,也就是说我们可以访问到对象上并不存在的属性,只要其原型上有,我们可以利用这一点将属性和方法都赋值到对象的原型上,然后调用这些属性,好像它们存在那个对象上一样,如果几个对象共享相同的原型,我们给原型赋值之后,这几个对象就可以都访问了,无需将这些属性单独拷贝到每一个对象上,这就是原型继承(如果我的对象没有,但对象原型有,那我的对象也可以拿到这个属性)事实上这里并没有发生继承,在面向类的语言里,继承是指父类复制属性到子类的行为,在js中并没有这种操作,这也就是原型继承与类继承的一个区别
设置对象原型:我们已经知道基本每个对象都有原型,那么函数呢,原型又是从哪里来呢?来自于Object的函数,在js引擎执行程序之前,会创建一个环境让程序在内部执行,在执行环境中创建一个函数,叫做Object,并为该函数关联一个对象Object.prototype,换句话说Object和Object.prototype在任意执行中的js程序中永远存在,这个Object乍一看和其他函数没有区别,但特别之处在于它是一个构造器,在调用时返回新对象

image.png
函数有一个叫做.prototype的属性指向对象Object.prorotype 2.Obejct.prototype对象有一个叫做constructor的属性指向Object函数。实际上这个方案对js中的所有函数都是适用的)

image.png
对于所有函数需要记住以下(1.所有的函数都有.prototype属性,它指向这个函数的原型对象,2.原型对象都有.constructor的属性,它指向函数本身,3.一个函数的原型.constructor并非必须指向该函数,.prototype同理)
设置函数原型对象有一定的规则(1.默认 2.使用new隐式设置原型 3.使用Object.create显示设置原型)
默认规则:对象字面量const fn = {} js在幕后将对象的原型指向Object,prototype,并设置其原型的.constructor指向Object函数
使用new隐式设置原型:普通函数作为构造函数使用 function fn(){} var fnObj = new fn() 我们知道fn这个函数将会和fn.prototype对象关联,当我们使用new关键字创建一个对象时.js将会(1.设置这个新对象的原型指向我们使用new调用函数的.prototype属性 2.设置原型.constructor属性指向我们使用new调用的构造函数)
使用Object.create显示设置原型:最后我们可以手动设置对象的原型引用

image.png
最后我们使用原型来模拟面向类的行为

image.png
上面的图就是整个原型链的连接过程,这里没有画完(最后Object.prototype.constructor指向Object函数,Obejct.prototype.proto指向null)
让开发者使用这种方法创建对象是在是太痛苦了,于是有了class
5.深入探究类
经过上面的阅读,我们了解了原型系统,我们来看一下class(类构造器/静态方法/原型方法)
类构造器constructor用于我们初始化逻辑(1.只能在constructor中调用父类的构造器 2.它在背后做了所有设置原型链的操作 3.constructor被用作类的定义,4.子类的原型就是超类 5.类必须使用new关键字来调用,类的本质就是函数,函数不用new调用时没有默认返回的话将返回undefined而类则将报语法错误)

image.png
静态方法:静态方法是类自己的方法。不能被类的实例化对象调用,我们使用static来定义

image.png
原型方法:任何不是构造方法和静态方法都是原型方法,也就是说我们在类中定义的普通方法将会被挂在原型上
6.Map和WeakMap Set和WeakSet
Map:就是键值对。最简单的理解方式是它类似于Object,它的独特之处在于key可以设置为Object而不仅仅是字符串,Map会将我们设置进去的一个个键值对弄成数组的形式如下图

image.png
Map:set get has delete clear 并且可以遍历
WeakMap:set get has delete 不可以遍历,不能遍历的原因是在遍历过程中遇到垃圾回收有可能这一秒是满的下一秒就没了
Set是只能包含一个值的集合,最常用于去除数组的重复元素[...new Set([1,2,3,4])] Set和Map拥有相同的api,但是Set没有set方法,取而代之的是add
Set:add has delete clear 可以遍历
其实WeakMap和WeakSet我并不了解,会的就这些了
5.Promise用法讲解
Promise是一个构造函数,自己身上有all/reject/resolve这几个眼熟的方法,原型上有then/catch等同样眼熟的方法,Promise的构造函数接收的是一个函数参数,并且该函数传入两个参数(resolve,reject)分别表示异步操作执行成功之后的回调函数和异步操作失败执行的回调函数

image.png

image.png

image.png

image.png

image.png
Promise就是我们可以在then方法中继续写Promise对象并返回

image.png
现在我们对Promise应该有个轮廓了吧,reject同resolve是一样的。再失败时回调

image.png

image.png
all的用法:all接收一个数组,并行执行多个异步操作。里面的值都返回Promise对象,该方法如果遇到异步返回失败将只会返回异步失败的第一个函数调用返回内容,并且失败后面的的函数将不再执行,如下图要在三个异步操作都结束之后才会进入then里面,那么三个异步操作返回的数据也就都在then里面,all把所有的结果放进一个数组传给then,也就是results

image.png

image.png

image.png

image.png
javascript学习(ECMAScript,DOM,BOM)https://www.jianshu.com/p/b7136540b379