微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

概念

HTML

js基础

表达式中变量求值规则

无论结合性和优先级如何,求值顺序总是从左到右。

运算符优先级

. () [] new ?. fn()
+... -... 前置递增递减
/ %
+ -
&&
||
三元表达式
=
,

运算符规则

逻辑运算符规则

&& 左侧为真,则以右侧式子的值为整个表达式的值,左侧为假,则以左侧式子的值为整个表达式的值,右侧不执行;

|| 左侧为真,则以左侧式子的值为整个表达式的值,右侧不执行;左侧为假,则以右侧式子的值为整个表达式的值;

! ,!后面跟的一定是一个布尔值,如果不是先转成布尔值,再执行! 的取反,引用数据类型取反一定为false

引用数据类型判断规则

调用valueOf,有基本值就用基本值判断,没有基本值再调用toString然后再去判断

判断运算符规则

如果两边类型相等,则直接判断,如果两边类型不等,转换为数字

特殊情况:

  • null除了与undefined相等(不全等)外,都不相等
    因为undefined派生自null

  • NaN进行计算时都为NaN,进行判等与比较时都为false(包括NaN自己)

  • 相加时

    • 如果有字符串,则为拼接
    • 如果有引用数据类型,则把引用数据类型按引用数据类型判断规则转换后再进行拼接
    • 其他情况全部转数字再相加
  • 比较时

    • 如果两边都是字符串则比较字符串的Unicode码
    • 如果有引用数据类型,则把引用数据类型按引用数据类型判断规则转换后再进行比较
    • 其他情况全部转数字再比较
  • 双等判断时

    • 如果两边都是引用数据类型,比较的是地址
    • 如果两边都是字符串,比较的是Unicode码
    • 如果只有一边是引用数据类型,则把引用数据类型按引用数据类型判断规则转换后再比较
    • 其他情况全部转数字

作用域链

每个javascript函数都是一个对象,对象中有些属性我们可以访问,比如name属性,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个,,即ES6中所说的对外部环境的引用,指的就是我们所说的作用域,其中存储了执行期上下文的集合。这个集合呈链式链接,我们把这种链式链接叫做作用域链

作用域链的前端,始终都是当前执行的代码所在环境的变量对象。
全局执行环境的变量对象始终都是作用域链中的最后一个对象。

原型链

每个实例身上都有个隐式原型对象指向创建它的构造函数显示原型对象

原型链就是实例和创建它的构造函数间的链接形成的链式结构

原型链的终点是null

执行上下文

上下文又叫环境

什么是执行上下文

执行上下文是ECMAScript规范中用来描述 JavaScript 代码执行的抽象概念,规定了当前代码执行的环境(当前执行代码片段中的变量、函数、作用域链等)

执行上下文栈

执行上下文栈用来管理执行上下文。

执行上下文栈也叫调用栈,执行上下文栈用于存储代码执行期间创建的所有上下文,具有LIFO(Last In First Out后进先出,也就是先进后出)的特性。

JS代码首次运行,都会先创建一个全局执行上下文并压入到执行上下文栈中,之后每当有函数调用,都会创建一个新的函数执行上下文并压入栈内;由于执行栈LIFO的特性,所以可以理解为,JS代码执行完毕前在执行栈底部永远有个全局执行上下文。

可以把执行栈认为是一个存储函数调用的栈结构,遵循先进后出的原则

执行上下文创建

执行上下文分为创建阶段执行阶段

执行上下文创建阶段负责三件事:

  • 确定this
  • 创建词法环境组件
  • 创建变量环境组件

词法环境与变量环境

词法环境

是Js引擎内部用来跟踪标识符和特定变量之间的映射关系

活跃对象变量对象(ES3)又叫词法环境变量环境(ES5+)

在ES5后,已经不存在活跃对象的定义了,现如今活跃对象只是广义的抽象,而不再是狭义的定义了

词法环境分为全局词法环境与函数词法环境

词法环境中有两个组成部分:

  • 环境记录: 储存变量和函数声明的实际位置
  • 对外部环境的引用:当前可以访问的外部词法环境(全局词法环境对外部环境的引入记录为null)

变量环境也是一个词法环境,他具有词法环境中所有的属性,区别如下:

  • 词法环境存储函数声明和变量let和const绑定
  • 变量环境仅用于变量var绑定

活跃对象是作用域链上正在被执行和引用的变量对象

全局执行上下文

全局执行上下文只有一个,在客户端中一般由浏览器创建,也就是我们熟知的window对象,我们能通过this直接访问到它。

全局对象window上预定义了大量的方法属性,我们在全局环境的任意处都能直接访问这些属性方法,同时window对象还'var声明的全局变量的载体。我们通过var创建的全局对象,都可以通过window直接访问。

函数执行上下文

函数执行上下文可存在无数个,每当一个函数调用时都会创建一个函数执行上下文;需要注意的是,一个函数被多次调用,都会创建一个新的上下文。

代码分析

function f1() {
    f2();
    console.log(1);
};

function f2() {
    f3();
    console.log(2);
};

function f3() {
    console.log(3);
};

f1();//3 2 1

执行过程

//代码执行前创建全局执行上下文
// f1调用
// f1又调用了f2,f2执行完毕之前无法console 1
// f2又调用了f3,f3执行完毕之前无法console 2
// f3执行完毕,输出3并出栈
// f2执行完毕,输出2并出栈
// f1执行完毕,输出1并出栈
// 此时执行栈中只剩下一个全局执行上下文

总结

执行上下文是ECMAScript规范中用来描述 JavaScript 代码执行的抽象概念,规定了当前代码执行的环境(当前执行代码片段中的变量、函数、作用域链等)

执行上下文主要干了如下事:

  1. 创建执行环境

    • 确定this指向
    • 创建词法环境组件
    • 创建变量环境组件
    • 其中词法环境与变量环境
      • 确定当前执行环境的作用域(对外部环境的引用)
      • 收集变量和函数声明形成一个对象(环境记录) --> 预解析

    词法环境分为全局词法环境与函数词法环境,是Js引擎内部用来跟踪标识符和特定变量之间的映射关系

    词法环境中有两个组成部分:

    • 环境记录(是一个对象): 储存变量和函数声明的实际位置
    • 对外部环境的引用:当前可以访问的外部词法环境(全局词法环境对外部环境的引入记录为null)

    变量环境也是一个词法环境,他具有词法环境中所有的属性,区别如下:

    • 词法环境存储函数声明和变量let和const绑定
    • 变量环境仅用于变量var绑定
  2. 执行执行环境中代码

1.全局执行上下文一般由浏览器创建(即window对象),代码执行时就会创建;(所以执行完毕前在执行栈底部永远有个全局执行上下文)

2.函数执行上下文只有函数调用时才会创建,调用多少次函数就会创建多少上下文。

3.执行上下文栈(调用栈)用于存放所有执行上下文,满足FILO规则(先入后出)

this

  • 强制绑定:首先判断是否存在 bind、apply、call 等方法,如果有,则指向当前方法的第一个参数
  • 实例化调用:再判断是否是 new 调用函数,如果是,则指向实例化对象
  • 隐式绑定(上下文对象调用):类似于 a.b() 此时 this 指向上下文对象
  • 认绑定:即没有明显调用者,严格模式指向undefined,js环境指向window,node环境下指向global
  • 隐式丢失: 类似于 a = b.fn; a();要观察丢失以后,函数是怎么调用
  • 箭头函数没有自己的this,它的this继承于它的外层作用域

堆和栈的概念

数据结构的两种,专门用来解决数据的存储和读取

内存:栈内存和堆内存(其它语言),js当中只有堆内存(堆内存又分为栈结构和堆结构,我们相当于用的栈结构和堆结构)

​ 栈内存比较小,但是快

​ 堆内存比较大,但是慢

拓展: 队列结构   先进先出  管状结构

​ 栈:栈的数据结构 FILO先进后出(有序)

​ 堆:链表的数据结构,无序的可以随意添加删除

手写new

//new关键字法	
    function fn(a,b,c){
    }
    function new1(fn,...str){
    	//1.创建一个空的简单JavaScript对象(即`{}`)
        let obj={};
        //2.为步骤1新创建的对象添加属性`__proto__`,将该属性链接至构造函数的原型对象 
        obj.__proto__=fn.prototype;
        //3.将步骤1新创建的对象作为`this`的上下文
        let result=fn.bind(obj)(...str);
        //4.如果该函数没有返回对象,则返回`this`
        return typeof result===('object'||'function'&&result!==null)?result:obj;
    }
    
    
//new fn()法
let newMethod = function (Parent, ...rest) {
    // 1.以构造函数的prototype属性为原型,创建新对象;
    let child = Object.create(Parent.prototype);
    // 2.使用指定的参数调用构造函数,并将 `this` 绑定到新创建的对象
    let result = Parent.bind(child)(...rest);
    // 3.如果构造函数没有手动返回对象(引用类型),则返回第一步创建的对象(实例),否则返回手动设置的返回值
    return typeof result===('object'||'function'&&result!==null) ? result : child;
};

手写call

  /*
	call:
    1:调用函数
    2:改变函数的this指向为call的第一个参数
    3:返回函数的返回值
  */
Function.prototype.myCall = function (context, ...argv) {
  /* 
  	context有3种情况
      - null undefined ----> window
      - 基本包装类型----> 包装对象
      - 对象类型---->保持
  */
  if (context === null || context === undefined) {
    context = window;
  }
  if (typeof context !== 'object' && typeof context !== "function") {
    context = Object(context);
  }
  //this是被mycall调用改变this的那个函数
  //想要让this代表的函数中的this指向context,需要给context扩展一个方法值为this这个函数
  const sy1 = Symbol();
  context[sy1] = this;
  //调用函数(真正调用的是this指向的那个函数,但是这个函数的this已经指向了context),并且拿到this代表的函数的返回值
  const re = context[sy1](...argv)
  //不影响原来的对象,所以把我们设置的属性删除掉
  delete context[sy1];
  //call返回 被调用函数的返回值
  return re;
}

错误类型

ECMA-262规范了7种错误类型。其中Error是基类,其他6种错误类型是子类,都继承了基类。Error类型的主要作用是自定义错误对象。

  1. Error:普通异常 。与thorw语句和try/catch语句一起使用

  2. EvalError:不正确使用eval()方法时抛出

  3. SyntaxError:出现语法错误时抛出

  4. RangeError:数字超出合法范围之抛出

  5. ReferenceError:读取不存在的变量时抛出

  6. TypeError:值的类型发生错误的时候抛出

  7. URIError:URI编码和解码错误时抛出

  8. AggregateError()`构造函数可以接受两个参数。

    • errors:数组,它的每个成员都是一个错误对象。该参数是必须的。
    • message:字符串,表示 AggregateError 抛出时的提示信息。该参数是可选的。
    const error = new AggregateError([
      new Error('ERROR_11112'),
      new TypeError('First name must be a string'),
      new RangeError('Transaction value must be at least 1'),
      new URIError('User profile link must be https'),
    ], 'Transaction cannot be processed')
    

基本类型值

基本类型值都是按值访问 直接操作保存在变量中的实际值
基本类型值都是存储在栈区中,我们可以直接通过变量名访问实际值

  • 基本类型值的特点:

    1. 基本类型的值是不可变的(我们平时修改的是变量的值,并不是直接修改基本类型的值)
    2. 我们不能给基本类型值添加属性方法 就算添加也是获取不到的
    3. 基本类型的比较是值的比较
    4. 基本类型的变量是存放在栈区的(栈区指内存里的栈内存),栈区包括了变量的标识符和变量的值

引用类型值

  • 引用类型值在栈区储存的是 标识符(变量名)和引用地址 在堆区储存的是对象的值

  • 当我们访问某一个对象的时候,要先访问到栈区的地址 然后引用到堆区的值

  • 引用类型值特点:

    1. 引用类型的值是可变的,我们可以为引用类型添加属性方法
    2. 引用类型的值是同时保存在栈内存和堆内存中的对象
    3. 引用类型的比较是引用的比较

各种类型的构造函数创建方式

包装对象的创建方式

new Number()
new String()
new Boolean()
new Object()
//使用valueOf()获取包装对象的值

数组

new Array('值1',...,'值n');
//如果不传参则创建空数组

函数

new Function('p1',...'pn','body')
//p1-pn表示所创建函数的参数名称列表,body表示所创建函数函数结构体语句,语句之间以分号分隔。

原生构造函数

  • Boolean()
  • Number()
  • String()
  • Array()
  • Date()
  • Function()
  • RegExp()
  • Error()
  • Object()

活动对象

活动对象就是当执行环境创建时执行环境内的变量对象被激活后的叫法

活动对象就是作用域链上正在被执行和引用的变量对象。

活动对象是在进入函数执行环境时刻被创建的,它能通过函数的arguments属性初始化。只有被激活的变量对象(活动对象),活动对象的各种属性才能被访问

垃圾回收机制

堆空间的释放是靠垃圾回收机制进行的

  1. 在js中,如果一个对象不再被引用,那么这个对象就会被回收
  2. 如果两个对象相互引用,但不被第三者所引用,那么这两个相互引用的对象也会被回收

当程序函数或者整个程序执行完成后,栈里面所有的东西都被释放销毁,堆当中的数据可能还在,只是没有任何的变量指向(引用),那么堆当中的数据就会变成垃圾对象。回收机制会在适当的时候将垃圾对象清理回收;

如果我们在程序当中需要去删除对象,那么就将这个对象的变量赋值为null,代表这个对象引用为空,这个对象也就成了垃圾对象,其实删除对象就是让堆当中的对象数据成为垃圾对象;

函数相关

函数直接量

  1. 函数直接量也称为匿名函数
  2. 匿名函数可以自己调用,比如加上小括号然后整体调用,或者在最前边添加!-+~一元操作符

获取参数的个数

获取实参个数:使用arguments对象的length属性

获取形参个数:使用函数对象的length属性

IIFE(立即执行函数)

callee

callee是arguments对象的属性,它引用当前argument对象所在的函数,使用该属性可以在函数体内调用自身。

//判断形参与实参个数是否相同
function f4(a,b,c,d) {
    if (arguments.length != arguments.callee.length) {
        throw new Error("参数不一致");
    }else{
        alert("go")
    }
}
f4(1,2,3,4,5)

函数执行过程

  1. 函数创建一个执行环境

  2. 复制函数的 [[scopes]] 属性中的对象构建起执行环境的作用链域

  3. 创建函数活跃对象并推入执行环境作用链域的前端

  4. 执行代码

  5. 销毁执行环境和活跃对象(闭包情况下活跃对象仍被引用没被销毁)

闭包

闭包函数就是拥有外层函数活动对象引用的函数对象,用于封存外部函数活动对象的活动状态

js的执行环境本身就是一个全局的作用域,我们写得每一个js函数只要引用了全局的活动对象就算闭包

我们通常说的闭包是函数嵌套函数

垃圾回收机制与堆栈存储有关

函数执行完毕后,函数的执行环境将被立即销毁,但其在上下文创建阶段在堆中开辟的用于存储变量的环境记录未被立即销毁,环境记录中的变量只要还被引用,就不会被销毁,就形成了闭包

函数

一个函数的返回值只依赖其参数,并且对外部没有副作用(没有修改外部的变量)

let fn=(a,b)=>a+b //纯函数
let fn=(a,b)=>a+b+1 //非纯函数

高阶函数

满足以下两个条件其中之一就是高阶函数

​ 接收的参数是个函数

​ 返回的是个函数

柯里化

一个具有多个参数的函数转变为多层嵌套的函数:

  1. 每层嵌套只使用一个参数,并返回使用剩余参数的函数
  2. 直至参数使用完毕,返回使用所有参数后的结果

这个过程称为柯里化

函数的入口出口

函数提供两个接口与外界的交互,

其中参数作为入口,接受外界的信息。

返回值作为出口,把运算结果反馈给外界。

封装

隐藏对象的属性和实现细节,仅对外公开接口

继承

使得子类对象(实例)具有父类属性方法

原型链继承:子类构造函数显示原型作为父类构造函数的实例

借用构造函数继承:使父类构造函数指向子类构造函数实例

组合继承:以上两种继承的结合,子类构造函数显示原型作为父类构造函数的实例,并使父类构造函数指向子类构造函数实例

原型式继承:通过Object.create深拷贝对象赋值给另外一个对象

寄生式继承:通过Object.create深拷贝对象,并对这个深拷贝出来的对象进行扩展,然后赋值给另外一个对象

寄生式组合继承:通过Object.create深拷贝父类的原型对象,并对这个深拷贝出来的对象进行扩展,然后赋值给子类的原型对象

寄生式组合继承:

多态

子类在继承父类后重写和重载覆盖了父类方法就是多态的一种体现

方法重载

父类的同一个方法根据参数的不同来执行不同的业务逻辑

方法重写

父类方法同名,但功能不同,被称作方法重写

继承的代码示例

  • 原型继承

    父类的实例作为子类的原型,将子类的原型构造器补充完整 (为了让子类继承方法

<body>
    <script>
        //父类
        function Person(){

        }
        //子类
        function Student(name,age,sex){
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        //原型继承
        Student.prototype = new Person();
		//因为上一步赋值导致Student的prototype中的构造器丢失
        Student.prototype.constructor = Student;
        //实例化子类对象 
        var s1 = new Student("王五",25,"男");
        console.log(s1); //Student {name: '王五', age: 25, sex: '男'}
    </script>
</body>
<body>
    <script>
        //父类
        function Person(){
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        //子类
        function Student(name,age,sex){
        	//使用构造函数方式继承
            Person.call(this,name,age,sex);
        }
        //实例化子类对象 
        var s1 = new Student("王五",25,"男");
        console.log(s1); //Student {name: '王五', age: 25, sex: '男'}
    </script>
</body>
  • 组合方式继承

    原型链加构造函数组合继承

<body>
    <script>
        //父类
        function Person(){
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        //子类
        function Student(name,age,sex){
        	//使用构造函数方式继承
            Person.call(this,name,age,sex);
        }
        //原型继承
        Student.prototype = new Person();
		//因为上一步赋值导致Student的prototype中的构造器丢失
        Student.prototype.constructor = Student;
        
        //实例化子类对象 
        var s1 = new Student("王五",25,"男");
        console.log(s1); //Student {name: '王五', age: 25, sex: '男'}
    </script>
</body>

多态的代码示例

<body>
    <script>
        //父类
        function Person(name,age,sex){
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        Person.prototype.eat = function(num){
            if(typeof num  == "number"){
                console.log('一天吃'+num+'顿');
            }else{
                console.log('今天吃了');
            }
        }
        var p1 = new Person();
        p1.eat(); //今天吃了
        p1.eat(5); //一天吃5顿
    </script>
</body>
<body>
    <script>
        //父类
        function Person(name,age,sex){
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.eat = function(){
                console.log('吃了吗');
            }
        }
        //子类
        function Student(){
            this.eat = function(){
                console.log('吃了您嘞');
            }
        }
        Student.prototype = new Person();
        Student.prototype.constructor = Student;
        var s1 = new Student();
        s1.eat();  //吃了您嘞
    </script>
</body>

数组相关

多维数组

javascript不支持多维数组,设置元素的值等于数组,可以模拟二维数组结构,如果二维组中每个元素的值也为数组,则可以模拟三维数组,以此类推,通过数组套的形式可以定义多维数组
读写多维数组方法普通方法相同,都是使用中括号进行访问

关联数组

  • 如果数组的下标值超出范围,如负数、浮点数、布尔值、对象或其他值,js会自动把它转换为一个字符串,并定义为关联数组。

  • 关联数组就是与数组关联的对象,简单地说就是数组对象,字符串下标就是数组对象的属性

    var arr12 = [1,2];
    arr12[3.1] = "haha";//相当于给数组扩展了一些属性方法
    arr12[true] = "buer";
    console.log(arr12);
    console.log(arr12[3.1]);
    

对象相关

Object.prototype.toString为什么能判断数据类型

对象在创建之时,会用内部属性[[Class]]记录对象创建时的类型,而且这个内部属性无法被外部程序访问,这意味着它很安全,Object.prototype.toString就是利用这个内部属性来检测对象类型的。

数据属性和访问器属性

数据属性

数据属性:包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个描述其行为的特性:

访问器与访问器属性

访问器分为读访问器(getter)与写访问器(setter)

访问器属性:这个属性不包含数据值,包含一对get和set方法,在读写访问器属性时,就是通过这两个方法来进行操作处理的。

访问器属性只能设置如下四种属性,不能给访问器属性定义writeable和value

成员访问

通过点或中括号操作符访问属性称为成员访问

.称为成员访问

[]称为需计算的成员访问

需要计算或直接使用变量需要使用[]操作符

包装对象

存取字符串、数字或布尔值的属性时,创建的临时对象称为包装对象

delete运算符

delete运算符用于删除变量与值之间的连接,并不是之间删除

var a={p:{x:1}};
var b=a.p;
delete a.p;
console.log(b); //{x:1}

JSON.stringfiy

对象key的value为undefined,Function,Symbol的时候,转为JSON会直接忽略该key除非JSON.stringify第二个参数指定

对象的key为Symbol的时候,会被完全忽略(JSON.stringify第二个参数无法指定)

数组里的值为undefined,Function,Symbol时,转换为null

JSON.stringify(value[, replacer[, space]])
replacer:
可选。用于额外转换的函数或指定转换成员的数组。
    如果为额外转换的函数:传入每个成员的键和值,函数的返回值作为该成员转换后的值。如果此函数返回 undefined,则排除成员。
    如果为指定转换成员的数组:按照数组中key值的顺序依次转换对象中指定的成员,key值不在数组中的成员不转换
space:
可选,添加缩进和换行符,值为数字,代表缩进指定数量的空格,如果space大于10,则缩进10个空格。
space 也可以使用非数字,如:\t。

深浅拷贝

浅拷贝是拷贝的地址

var obj={};		//obj=0x100;
a=obj;			//a=0x100;

深拷贝是拷贝每一个

var arr=[1,2];	//arr=0x100;
var b=[];		//b=0x200;
b[0]=arr[0];	//b[0]=1;
b[1]=arr[1];	//b[1]=2;

防抖

n 秒内连续触发事件时,只执行最后一次触发的事件回调函数

如果设定时间到来之前,又触发了事件,就重新开始延时。

节流

n 秒内连续触发事件时,只执行第一次触发的事件回调函数

ES6

super关键字

super指向当前方法的隐式原型属性(__proto__)

new.target属性

  1. 属性一般用在构造函数之中,返回new命令作用于的那个构造函数
  2. 如果构造函数不是通过new命令调用的,则返回undefined
  3. Class 内部调用new.target,返回当前 Class。

class

ES6 引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

  1. 构造函数的静态的方法属性 直接用构造函数调用 使用static定义 ,如果不加static 则声明的属性方法是构造器(constructor方法)的
  2. 静态的方法属性相当于直接给添加方法属性(person.属性方法)
  3. constructor方法是类的方法通过new命令生成对象实例时,自动调用方法一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被添加
  4. constructor方法中的this指向实例,并且类被new调用时传入的参数会传入到constructor方法
  5. constructor方法中的super方法相当于调用父类constructor方法
  6. 在类中写在constructor方法外的其他属性(属性=值)相当于constructor方法中写了一个this.属性=固定值
  7. 在类中写在constructor方法外的其他方法(方法名())相当于**给当前类写了一个原型对象(prototype)上的方法

Promise

谈一谈promise

为什么要有promise?

回调地狱这种回调函数嵌套回调函数的方式使得代码层层嵌套,导致代码臃肿,可读性差,耦合度高,程序难以维护

promise标准化一定程度上解决了js的这种流程操作问题.

promise是什么?

promise是一种异步编程的解决方案,可以使异步操作以同步的流程表现出来,避免了回调地狱(层层嵌套的回调函数)

代码呈现上来讲,

promise是一个构造函数,需要实例化使用,具有all allsettled any race resolve reject 6种静态方法

其原型对象上具有then catch finally 三种方法

其实例上具有PromiseState,PromiseResult两种属性分别代表 promise实例的状态,promise实例的值,

其中promise实例的状态分为三种,pending,fulfilled,rejected,只有promise的状态从pending改变,就称为已敲定(settled)

promise实例的值就是调用resolve或reject方法时传入的参数

谈一谈then

方法本身来讲

then方法是promise原型对象上的方法

方法具有两个参数回调函数,分别用来处理调用它的promise实例的成功和失败状态

参数回调函数接收一个参数,参数的值是promise实例的值

从返回值来讲

  • 当then方法的具有处理调用方法的promise实例的参数回调函数时,

    • 如果返回值是promise实例对象,则then方法返回与该参数回调函数的值和状态等同promise实例对象

    • 如果返回值不是promise实例对象,

      • /且该参数回调函数不报错,则then方法返回fulfilled状态的promise实例对象,值是该参数回调函数的返回值
      • 如果报错,则then方法返回rejected状态的promise实例对象,值是错误信息
  • 当then方法不具有处理调用方法的promise实例的参数回调函数时,会发生值穿透,

    • 即返回与调用then方法的promise实例对象值和状态等同的promise实例对象

    值穿透

    then或catch不具有处理调用它的promise实例对象状态(包括pending状态)的参数回调函数时,返回与调用then或catch实例对象状态和值等同的promise实例对象

谈一谈catch

catch是promise原型对象上的方法

其具有一个参数回调函数,用来处理promise实例对象的失败状态

其返回值规则与then等同

谈一谈finally

finally是promise原型对象上的方法

具有一个参数回调函数

除了报错和返回失败的promise实例,其他情况都穿透

promise的6个静态方法

resolve

快速得到一个成功状态的promise实例对象

如果resolve函数里的参数是promise实例,则返回这个promise实例

如果resolve函数的参数不是promise实例,则返回成功状态的promise实例,值是调用resolve时传的参数

    let p1=Promise.resolve('1');
    let p2=Promise.reject('2');
    console.log(Promise.resolve(p1));
    console.log(Promise.resolve(p1)===p1);
    console.log(Promise.resolve(p2));
    console.log(Promise.resolve('3'));
    值开始是undefined,然后变成调用resolve时传入的参数
    let p3=new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve('2');
        }, 3000);
    })
    console.log(Promise.resolve(p3));

reject

快速得到一个失败状态的promise实例对象

返回一个失败状态的promise实例对

    let p1=Promise.resolve('1');
    let p2=Promise.reject('2');
    console.log(Promise.reject(p1));
    console.log(Promise.reject(p2));;
    console.log(Promise.reject('3'));
    值开始是pending状态的promise实例,然后变成fulfilled状态的promise实例
    let p3=new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve('2');
        }, 3000);
    })
    console.log(Promise.reject(p3));

all

接收一个具有iterator接口的参数,

认返回pending状态的promise实例

当参数接收的所有promise实例都变成成功状态时,返回成功状态的promise实例,值是参数接收的所有promise实例里的值组成的数组

当参数接收的promise实例有变成失败状态时,返回失败状态的promise实例,值为参数接收的promise实例里面第一个变成失败状态的promise实例的错误信息

	let p1=Promise.resolve('1');
    let p2=Promise.resolve('2');
    let p3=Promise.reject('3');
    let p4=Promise.reject('4');
    console.log(Promise.all([p1,p2]));
    console.log(Promise.all([p1,p2,p3,p4]));

allSettled:

成功的promise被称作为已兑现,失败的promise被称作为已拒绝,无论成功还是失败,只要状态不是pending,则都被称作为已敲定(settled)

认返回pending状态的promise实例,只有当所有的promise实例都已经敲定,则allSettled返回成功的promise实例

allSettled值 是所有promise实例状态和值组成的对象按顺序组成的数组

	let p1=new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve(1);
        }, 300);
    })

    let p2=new Promise((resolve,reject)=>{
        setTimeout(() => {
            reject(2);
        }, 500);
    })

any

认返回pending状态的promise实例

返回与参数中promise实例中最先成功的promise实例值和状态等同的promise实例

promise.any()

race

认返回pending状态的promise实例
​ 当参数中的promise实例有结果了,返回与参数中promise实例中最先出结果的promise实例值和状态等同的promise实例

    let p1=new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve(1);
        }, 300);
    })

    let p2=new Promise((resolve,reject)=>{
        setTimeout(() => {
            reject(2);
        }, 500);
    })
    console.log(Promise.race([p1,p2]));

async和await

解决异步编程问题的终极解决方案,用同步方式表达异步操作

async用来声明某个函数是异步函数

async 函数认返回一个 Promise 实例对象,值为async函数的返回值

await讲解

await等待的是promise实例状态的敲定,即从pending状态变成resolved或rejected状态,

  • 如果await等待的promise实例状态敲定为成功,则继续向下运行
  • 如果await等待的promise实例状态敲定为失败,则退出整个函数
  • 如果await后面不是一个promise实例,await不在等待,向下执行

一个await表达式执行完毕前的代码是同步执行的,之后的代码是异步的

async function foo(){
 	await new Promise((resolve,reject)=>{};	
 	//因为js代码从上往下,从右往左执行,所以Promise构造函数在这里同步执行,不需要等待
 	await 异步操作;
}

代码分析

    (async function fn() {
        await setTimeout(() => {
            console.log(1);
        }, 3000);
        await setTimeout(() => {
            console.log(2);
        }, 2000);
        await setTimeout(() => {
            console.log(3);
        }, 1000);
    })();

类似这样

    new Promise((resolve) => {
        resolve(setTimeout(() => {
            console.log(1);
        }, 3000));
    }).then(() => {
        return setTimeout(() => {
            console.log(2);
        }, 2000);
    }).then(() => {
        return setTimeout(() => {
            console.log(3);
        }, 1000);
    })

预编译语言

预先编译再执行的语言,如css预编译less,sass,js

事件轮询与宏任务与微任务

什么是同步,什么是异步

  • 同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,后一个同步任务才能执行。
  • 异步任务是指不在主线程、而是在任务队列中的任务。只有当任务队列通知主线程,并且执行栈为空时,该任务队列中的任务才会进入主线程执行。

浏览器事件轮询机制:

  • 代码分类:同步代码,异步代码

  • js 执行代码的先后顺序:先异步(注意:整个 script 是异步的),再执行同步代码(script 内部的代码),再执行异步代码(常见的异步回调)

  • 浏览器事件模型的组成:主线程、浏览器的事件管理模块、回调队列

  • 事件轮询过程

    • 在执行主线程的过程中,如果遇到了异步代码,则把异步代码的回调函数交给浏览器的事件管理模块管理(DOM 管理模块\ajax 管理模块\定时器管理模块)
    • 浏览器的管理模块对事件进行监听,当回调函数已经达到条件需要执行的时候,会把回调函数依次放入回调队列(callback queue)
    • 当主线程的同步代码执行完毕,则会轮询回调队列,依次执行回调队列中的回调函数

nodejs 的事件轮询机制:

  • js 是单线程的,我们想要做到非阻塞,那么当我们遇到异步代码的时候,会把异步代码交给 libuv 的线程池进行管理,主线程依然继续向下执行同步代码
  • libuv 会开启多个线程去管理不同的异步代码,当异步代码需要被执行的时候,会把回调函数依次放入到 libuv 提供的对应的轮询队列中去,(因为 node 中异步种类繁多,libuv 提供了多种回调队列)
  • 回调队列的顺序和种类
    1. timers 阶段:
      处理定时器回调函数
    2. pending callbacks
      这个阶段用来处理系统操作的回调函数
    3. idle prepare 阶段
      此阶段是仅供 nodejs 内部操作调用
    4. poll 阶段
      这个阶段主要用来处理如 IO 操作,网络请求等异步操作
      当进入 poll 阶段之后,如果 poll 阶段为空,则除非 timers 阶段和 check 阶段有回调等待,否则就会一直等到 poll 阶段有新的回调执行
    5. check 阶段:
      这个阶段用来处理 setImmediate 的回调函数
    6. close callbacks 阶段
      这个阶段用来处理 close 事件

宏任务和微任务:

  • 异步代码也分为先后,先执行微任务再执行宏任务

  • 宏任务有:定时器\整个 script\io 操作等等

  • 微任务:promise 的 then\catch\finally process.nextTick queueMicrotask

  • 执行过程如下:

    1. 先执行主线程的同步代码直到结束,如果遇到微任务则放入微任务队列,如果遇到宏任务则交给对应的模块管理

    2. 检查微任务队列,并执行微任务中的回调函数,直到全部执行完成

    3. 执行宏任务队列中的异步代码,当执行的过程中遇到了微任务,则继续放入微任务队列

    4. 每次执行完一个宏任务的时候,都会再次检查微任务队列,执行微任务直到结束

    5. 继续轮询宏任务....

图解

image

数据交互相关

同源策略

同源策略(Same-Origin Policy)最早由 netscape 公司提出,是浏览器的一种安全策略。

浏览器执行服务端跨域请求响应回来的数据前需要遵循的标准

所谓的跨域错误指的是浏览器拒绝执行服务端响应回来的数据所抛出的错误

同源: 协议、域名、端口号 必须完全相同。

违背同源策略就是跨域。

如何解决跨域

JSONP

JSONP 是什么

一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持 get 请求。

JSONP 怎么工作的?

在网页有一些标签天生具有跨域能力,比如:img link iframe script。

JSONP 就是利用 script 标签的跨域能力来发送请求的

  1. 利用script标签向目标服务器传递一个字符串形式的属性,
  2. 目标服务器将该属性写成字符串形式的方法调用(等待该字符串传递回script标签在js标签内真正调用)
  3. 将要响应的数据以参数的形式传给该函数,然后以参数的形式响应回script标签,
  4. script标签自动执行响应回来的js脚本代码,并该script标签内书写这个方法,得到传入的数据

JSONP 的代码演示

1.动态的创建一个 script 标签
var script = document.createElement("script"); 
2.设置 script 的 src
script.src = "http://localhost:3000/testAJAX?callback=abc";  
3.服务器中返回一个方法,方法中包含响应数据
router.get("/testAJAX" , function (req , res) { 
    console.log("收到请求");
    var callback = req.query.callback; 
    var obj = {
        name:"孙悟空",
        age:18
    }
    res.send(`${callback}(${JSON.stringify(obj)})`);
});
4.在新定义的script标签前书写方法
function abc(data) {
   return data;
};
5.将 script 添加到 body 中 ,让新增的script标签访问服务器,拿到js代码,该代码用于调用abc方法
document.body.appendChild(script); 

CROS

CORS 是什么?

CORS(Cross-Origin Resource Sharing),跨域资源共享。

简单请求:

简单请求的跨域只需要服务端设置访问控制允许源(Access-Control-Allow-Origin)为请求的地址(如果请求地址写"*",则代表允许所有跨域访问)

  • get post初始是简单请求

  • http的头部信息不能超过以下几种字段 Accept Accept-Language Content-Type

  • Content-Type仅限于application/x-www-form-urlendcoded

复杂请求:

除了需要设置访问控制允许源为请求的地址外(如果请求地址写"*",则代表允许所有跨域访问)还需要给所有额外设置的请求头添加允许访问控制

  • put和delete初始是复杂请求
  • 所有的请求中,只要添加了额外的请求头,那这个请求也属于复杂请求

代理

代理服务器代替用户访问目标服务器

因为目标服务器返回的数据是返回给代理服务器,代理服务器再返回给浏览器的,所以不会发生跨域问题

重要报文与MIME类型

Content-Type是什么?作用?

实体头部 用于指示资源的MIME类型

MIME类型是什么?作用?

MIME类型也叫媒体类型,是一种标准,

用来指定文档、文件的格式

Content-Type常见的设置

  text/plain 纯文本
  text/html html文件
  text/css css文件
  application/javascript  js内容
  application/json json内容
  application/x-www-form-urlencoded form表单格式
image/gif gif格式图片
image/png png格式图片
image/jpeg jpg/jpeg格式图片
 video/mp4 视频格式
 audio/mp3 音频格式

状态码

1XX: 请求正在处理中

2XX: 请求成功

  • 200 : 请求成功
  • 201 : 请求成功,服务器创建了新的资源,常见post和put请求
  • 204 : 请求成功,服务器不需要向客户端返回报文实体

3XX: 重定向

4XX: 客户端错误

  • 400 : 客户端出现语法错误
  • 401 : 客户端没有权限,需要http认证
  • 403 : 服务端拒绝客户端的访问
  • 404 : 服务端没有找到对应的资源

5XX: 服务端错误

  • 500 : 服务端出现错误
  • 503 : 服务端超负荷或者宕机

从访问地址到最后展示的过程

  • 从url输入到最后展示的过程

    1. DNS查询 / 解析:将域名地址解析成ip地址
    2. TCP连接: TCP三次握手
    3. 发送请求
    4. 返回响应
    5. 渲染页面
    6. 断开连接:TCP四次挥手

http协议

协议是什么?

协议是指多台计算机相互之间进行网络通信时所必须共同遵守的规定或规则

http是什么?作用?

超文本传输协议,从万维网服务器传输超文本到本地浏览器的传输协议

规定了客户端与服务器之间进行数据传输时的报文格式,

我们将客户端向服务端发送的报文称为请求报文,

服务端向客户端发送的报文称为响应报文

https

HTTPS=HTTP+TLS(以前是SSL后来更改了)

三次握手

什么是三次握手?

TCP协议在发送数据前,通信双方必须在彼此间建立一条连接,三次握手就是在建立连接时发送的三次数据包

为什么要三次握手?

确保通信双方都能确定对方的接收和发送能力都正常

过程

SYN 发起一个新连接

ACK 确认有效,确认序号

Seq: 序号

  1. 客户端向服务端发送一个SYN(seq=j)数据包,请求客户端向服务端的连接
  2. 服务端接收到客户端发来的SYN数据包,确认客户端发送功能正常,向客户端发送一个SYN(seq=k)+ACK(seq=j+1)数据包,请求服务端向客户端的连接
  3. 客户端接收到服务端发来的SYN(seq=k)+ACK(seq=j+1)数据包,确认服务端发送与接收功能正常,向服务端发送ACK(seq=k+1)数据包
  4. 服务端接收到客户端发来的ACK(seq=k+1)数据包,确认客户端接收功能正常,建立客户端与服务端之间的连接

四次挥手

什么是四次挥手?

TCP的连接是双向的,所以需要在两个方向分别断开连接,

每个方向连接的断开又需要请求与确认,共需要发送四个包,因此称为四次挥手

四次挥手的过程?

Fin:释放连接

SYN 发起一个新连接

ACK 确认有效,确认序号

  1. 客户端向服务端发送FIN字段请求断开连接
  2. 服务端发送ACK字段答应断开连接请求,
  3. 此时服务端可能还有数据未处理完毕,等待数据处理完毕后,向客户端发送FIN字段请求断开连接
  4. 客户端发送ACK字段答应断开连接请求

浏览器渲染流程

  • 浏览器渲染流程:

    • 根据结构生成DOM树
    • 根据样式生成CSSOM树
    • 结合DOM树和CSSOM树生成render Tree(布局树 渲染树)
    • 对渲染树中的结构进行分层,生成图层树
    • 根据图层树,生成图层的绘制指令
    • 栅格化:将图层划分为图块
    • 绘制和展示

get和post的区别

  • GET和POST的区别 :
    • get是获取数据的,而post是提交数据的。
    • GET 可缓存, 而POST 不可缓存。
    • post发送的数据更大,get有url长度限制
    • post能发送更多的数据类型(get只能发送ASCII字符)

6种请求

扩展:resultful-->后端接口规范

GET 查 获取服务器上的资源,数据在地址上

POST 增 向服务器提交资源,如提交表单,上传文件等,可能导致新增资源或对原有资源进行修改,数据在请求体中

PUT 改 向指定资源位置上传更新其最新内容,数据在请求体中

DELETE 删 用来请求服务器删除某资源,数据在地址上

HEAD 检查资源或超链接的有效性或是否可以到达,数据在地址上

OPTIONS 预检请求,检查服务器支持,比如跨域时的预检等,数据在请求体中

存储相关

客户端存储:cookie

cookie的字段

使用服务端域名保存在浏览器端,同一域名下cookie的字段名不会重复,是唯一的,

请求时,会将客户端域名的所有cookie放在请求头中一同发送

字段名:值

httponly:是否只允许服务端访问

max-age:cookie生命周期 原生的单位为秒,cookie-parser中为毫秒

  • 如果设置为正值,指定时间后失效;
  • 如果设置为负值,关闭浏览器就失效;
  • 如果设置为0,则立即删除该Cookie。

特点

  1. cookie是明文发送的
    会随着http请求发送,如果过多可能会造成服务器压力
  2. cookie按服务端域名保存的
    保存在浏览器(客户)端,大小不超过4kb,有个数限制

过程

  1. 客户端向服务端发送请求
  2. 服务端对客户端的请求做出成功响应,响应头中携带cookie,cookie中保存着用户信息
  3. 客户端接收服务端的响应,并将cookie按服务端域名在客户端进行存储
  4. 客户端再次发送请求时,会自动在请求头中携带客户端域名下的所有cookie,一同发送到服务端
  5. 服务端接收客户端发送来的cookie,判断是哪个用户

本地存储:Web Storage

h5新特性之一,主要用于做本地存储,解决了cookie存储时一定会给服务器发请求的弊端
本地存储分为两种localstorage永久存储 sessionStorage临时存储/会话期存储

特点

  1. 大小不超过5M
  2. 不会随着http请求发送
  3. 操作简单
  4. 低版本ie不兼容

服务端存储:session:

特点

  1. 服务端存储,客户端存储session_id

设置过程

  1. 客户端向服务端发送请求
  2. 服务端接收到客户端的请求,验证成功后创建一个session对象和一个唯一的session_id,将当前用户信息和session_id存入session对象中
  3. 服务端向客户端发送响应,响应头中携带着存有session_id的cookie
  4. 客户端接收响应,并在客户端域名下存储该cookie
  5. 客户端再次发送请求时,会携带客户端域名下的所有cookie一同发送到服务端
  6. 服务端在cookie中查找session_id并去服务端查找具有该session_id的session对象中的用户信息并去服务器验证
  7. 如果确实存在该用户信息,响应成功,否则响应失败

token设置过程:

  • 客户端发送登录请求到服务端
  • 服务端接收到请求之后,和数据库进行比对,验证成功,则生成一个token字符串,响应给客户端
  • 客户端接收到token之后,会把token保存在localStorage或者cookie中
  • 客户端再次发送请求,会把token放在请求头中
  • 服务端解析请求头中的token,如果解析成功,就代表已经是登录状态

强制缓存和协商缓存

强制缓存:

强制缓存是客户端在浏览器缓存中查找请求结果,根据请求结果判断是否可以使用浏览器缓存的过程
简单来说就是浏览器使用自己的缓存,不需要向服务端发送请求

强制缓存的设置过程

客户端向服务端发送携带有Cache-Control字段值为max-age的请求头,max-age的单位是秒。表示多少秒内该缓存有效,无需向服务端发送新请求

服务端接收客户端的请求,向客户端发送携带有Cache-Control字段,值为max-age的响应头。max-age的单位为秒。表示多少秒内无需向服务端发送请求即可使用该缓存

当下次再次请求的时候,判断自己是否符合强制缓存条件,如果符合,则直接读取缓存,如果不符合,则会走协商缓存

协商缓存:

Etag / if-none-match优先级比 Last-Modified / If-Modified-Since 高。

Etag / if-none-match当前文件的唯一标识

Last-Modified / If-Modified-Since该资源的最后修改时间

过程

  • 第一次:由服务器通过响应头返回 Etag 和 Last-modified 字段
  • 第二次:浏览器(客户端)请求时,携带了if-none-match(值为上一次的Etag的值)和 If-None-Since(值为上一次的Last-modified的值)发送给服务器
  • 服务器检查if-none-match是否等于Etag的值,如果相等直接走浏览器本地缓存,不相等就返回新的资源
  • 如果没有if-none-match,才看If-None-Since的值,检查If-None-Since是否等于的Last-modified的值,如果都相同,则直接响应 304 状态码,要求读取缓存。否则响应数据,并携带最新的 eTag 和 last-modified

webpack的组成

  1. entry 入口 第一个被访问的源码文件。打包开始的地方,指示webpack应该使用哪个模块来作为构建其内部依赖关系图的开始。
    webpack可通过入口,加载整个项目的依赖
  2. output 出口 打包后输出的目录
  3. loader 加载器 将非js资源解析为webpack可识别的资源
  4. pugins 插件
    实现loader之外的功能
    是webpack的支撑,用来实现丰富的功能
  5. Mode:模式 生产模式 production 开发模式 development

node.js

Buffer

Buffer是专门用来保存二进制数据的

Buffer中存的是二进制的,打印Buffer会转成16进制显示,打印Buffer中的数据会转成10进制显示

  • 可以遍历Buffer中的每一个
  • Buffer可以调用toString方法将Buffer转成字符串

process

process 对象是一个全局变量,它提供有关当前 Node.js 进程的信息并对其进行控制

模块介绍与五个认形参

js文件被node启动时,这个js文件被称作一个模块

模块认被一个函数包裹,当js被node启动时,函数直接执行,并传入对应实参

这个函数具有以下形参:

exports 认指向module.exports初始指定的对象

require 引入模块的方法

module 当前module对象的详细内容

__filename 当前的文件绝对路径

__dirname 当前的文件所在的文件夹的绝对路径

图片懒加载

图片懒加载的实现原理:将图片的地址放在data-set属性中,由于图片并没有在src中,并不会发送http请求。比较图片到整个页面距离(Y)和 页面滑动的距离 (X) + 浏览器可视区域的大小(Z) ,Y小于两者之和,则图片显示出来,将图片的data-set属性换为src属性

 var imgs = document.querySelectorAll("img");  //获取所有的img元素
 window.onscroll = function(){     //定义触发事件
     var showViewH = document.documentElement.clientHeight;  //获取可视区域的高度
     //获取滚动的高度(兼容性写法)
     var scrollH = document.body.scrollTop || document.documentElement.scrollTop;  
     for(var i =0 ; i<imgs.length;i ++){
         //判断图片页面的高度与可视高度和滑动高度的大小
         if (imgs[i].offsetTop < showViewH + scrollH ) {  
             if (imgs[i].getAttribute('src') == '') {
                imgs[i].src = imgs[i].getAttribute('data-set');
             }
         }
     }
 }

react

React的理解,有什么特性

构建用户界面的js库,提供了UI层面的解决方案(DOM层面)

特性:

  1. 采用组件式开发(将界面分成一个个独立的小块,每个小块都是组件,这些组件共同组成页面),声明式编程规范(比如写(声明)一个DIV,就生成一个DIV),函数式编程,使开发更加高效
  2. 单向数据流,数据只能从高阶组件向低阶组件传递(通过props),子组件只允许对数据的读操作,不允许对数据的改操作

什么是虚拟DOM

函数式组件

使用大写字母开头的函数定义的组件

不能使用生命周期函数,

组合组件

组件的输出中引入其他组件

高阶组件(HOC)

接收一个组件,进行一些处理并返回新组件的函数称为高阶组件

用于权限控制,渲染劫持:等

缓存组件

当组件修改时,React会重新生成该组件的虚拟DOM,然后React 会调用render更新 DOM。

虽然这个比较很快。但是我们仍然可以加快这个过程

使用React.memo(组件)时,React 会得到该组件的虚拟DOM结果并记忆。组件修改后,如果新 旧虚拟DOM结果 相同,React 会重复使用记忆的结果,不使用render更新DOM。

React事件机制

当我们为事件添加事件回调时,React并没有将事件绑定在DOM上

而是根据事件类型生成合成对象,当事件冒泡至document时,React批量处理对应事件类型的合成对象中的事件回调

所以,当事件触发时,

React的组件通信

父传子:props

子传父:父通过props给子传函数,子通过函数传参传给父

状态提升

即多个组件需要反映相同的变化数据,我们将这个相同的变化数据写在最近的共同的祖先组件的状态中,而不是写在各个组件内的状态中,称为状态提升

路由懒加载

打包时忽略该路由组件,等到要用时,再进行编译并加载

使用lazy方法,需要使用<Suspense fallback={<h1>loading.....</h1>}>懒加载组件</Suspense>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐