前端知识点总结

291 阅读15分钟

html

Doctype作用?标准模式与兼容模式各有什么区别?

  1. 声明位于HTML文档的第一行,告知浏览器的解析器用什么文档标准解析这个文档。DOCTYPE不存在或格式不正确会导致文档以兼容模式呈现。

  2. 标准模式的排版和JS运作模式都是以该浏览器支持的最高标准运行。在兼容模式中,页面以宽松的向后兼容的方式显示,模拟老式浏览器的行为以防止站点无法工作。简单的说,就是尽可能的能显示东西给用户看。

HTML5 为什么只需要写 ?

HTML5 不基于 SGML,因此不需要对DTD进行引用,但是需要doctype来规范浏览器的行为(让浏览器按照它们应该的方式来运行);

而HTML4.01基于SGML,所以需要对DTD进行引用,才能告知浏览器文档所使用的文档类型。

HTML的语义化

语义化是指使用恰当语义的html标签,让页面具有良好的结构与含义;比如<p>标签就代表段落,<article>代表正文内容等等。 语义化的作用有两点:

什么是盒子模型?CSS-标准盒模型和怪异盒模型的区别?哪个css可以改变盒子模型?

css盒子模型 又称为框模型(Box Model),包含了元素内容(content)、内边距(padding)、边框(border)、外边距(margin)几个要素。

区别:当不对doctype进行定义时,会触发怪异模式。

在标准模式下,一个块的总宽度= width + margin(左右) + padding(左右) + border(左右) 在怪异模式下,一个块的总宽度= width + margin(左右)(即width已经包含了padding和border值)

javaScript

什么是闭包?闭包的优缺点

答:闭包是将外部作用域中的局部变量封闭起来的函数对象。被封闭起来的变量与封闭它的函数对象有相同的生命周期。 优点:一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除。

缺点:

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

地址栏输入一个URL后会发生什么?

参考 【peanutYu96】的文章: juejin.cn/post/684490…

js数据类型

1.基本类型

基本类型主要是: Undefined、Boolean、String、Number、Null、Symbol(ECMAScript 6 新定义)

  • 存放在栈中
  • 基本类型存储在栈内存中,数据大小确定,内存空间大小可以分配,按值存放,所以可直接访问
  • 基本类型的比较是值的比较

2. 引用类型

引用数据类型统称为 Object 对象,主要包括对象、数组、函数、日期和正则

  • 存放在堆中, 堆内存中是无序存放

引用类型存放在堆内存中,变量实际上是一个存放在栈内存的指针,这个指针指向堆内存中的地址。每个空间大小不一样,要根据情况进行特定的分配,例如:

var person1 = {name: 'joj'};
var person2 = {name: 'xiaomi'};
var person3 = {name: 'xiaoyang'};

  • 引用类型的比较是引用的比较

每次我们对js中的引用类型进行操作的时候,都是操作其对象的引用(保存在栈内存中的指针),所以比较两个引用类型,看是否指向同一个对象。例如:

var a = [1, 2, 3];
var b = [1, 2, 3];
console.log(a === b); //false

虽然变量a,b表示的是同一个内容,但其在内存中的位置不一样,指向的不是同一个对象,所以不相等。

数据类型的判断

  1. typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、symbol、object、undefined、function等7种数据类型,但不能判断null、array等
typeof Symbol(); // symbol 有效
typeof ''; // string 有效
typeof 1; // number 有效
typeof true; //boolean 有效
typeof undefined; //undefined 有效
typeof new Function(); // function 有效
typeof null; //object 无效
typeof [] ; //object 无效
typeof new Date(); //object 无效
typeof new RegExp(); //object 无效
  1. instanceof 用来判断A是否为B的实例,A instanceof B, 返回 boolean 值。instanceof 用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,但它不能检测 null 和 undefined
[] instanceof Array; //true
{} instanceof Object;//true
new Date() instanceof Date;//true
new RegExp() instanceof RegExp//true
null instanceof Null//报错
undefined instanceof undefined//报错
  1. 最准确最常用 Object.prototype.toString.call()
Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]

原型与继承

题目

请用js实现一个类P,包含成员变量a,成员变量b,成员函数sum,sum输出a与b的和,a,b默认值都为0。实现一个类M,M继承自P,在P的基础上增加成员变量c,成员函数sum变成输出a,b,c的和。

题目分析

Js所有的函数都有一个prototype属性,这个属性引用了一个对象,即原型对象,也简称原型。这个函数包括构造函数和普通函数,我们讲的更多是构造函数的原型,但是也不能否定普通函数也有原型,实现继承的方法很多,这里使用原型链和构造继承,即组合继承的方式。

function P(a, b) {
    this.a = a || 0;
    this.b = b || 0;
    this.sum = function() {
        return this.a + this.b;
    }
}
 
function M(a, b, c) {
    P.call(this, a, b) //继承P类的成员对象
    this.c = c; //在自己的构造函数中定义的
    this.sum = function() {
        return this.a + this.b + this.c;
    }
}
M.prototype = new P();
var m = new M(2, 2, 2);
M.sum(); //输出6

js继承的实现方式

既然要实现继承,那么首先我们得有一个父类,代码如下:

//定义一个动物类
function Animal(name, eye, skin) {
 
    //属性
    this.name = name || 'Animal';
    this.eye = eye;
    this.skin = skin;
 
    //实例方法
    this.sleep = function() {
        console.log(this.name + '正在睡觉');
    }
}
 
//原型方法
Animal.prototype.eat = function(food) {
    console.log(this.name + '正在吃:' + food);
};

下面给大家列出几种继承方式的实现

原型链继承

实现父类代码在(js继承的实现方式中)

核心: 将父类的实例作为子类的原型

function Cat() {}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';
 
//Test Code
var cat = new Cat();
console.log(cat.name);                     //cat
console.log(cat.eat('fish'));              //cat正在吃:fish
console.log(cat.sleep());                  //cat正在睡觉!
console.log(cat instanceof Animal);        //true
console.log(cat instanceof Cat);           //true
特点:

1. 非常纯粹的继承关系,实例是子类的实例,也是父类的实例

2. 父类新增原型方法/原型属性,子类都能访问到

3. 简单,易于实现

缺点:

1. 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中

2. 无法实现多继承

3. 来自原型对象的引用属性是所有实例共享的

4. 创建子类实例时,无法向父类构造函数传参(即无法像这样var cat=new Cat(hair,eye,skin)传参给父类)

推荐指数:★★(34两大致命缺陷)

构造继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
}
 
//Test Code
var cat = new Cat();
console.log(cat.name);                 //Tom
//console.log(cat.eat('fish'));        //报错
console.log(cat.sleep());              //Tom正在睡觉!
console.log(cat instanceof Animal);    //false
console.log(cat instanceof Cat);       //true
特点:

1. 解决了1中,子类实例共享父类引用属性的问题

2. 创建子类实例时,可以向父类传递参数(可通过Animal.call(this,name,eye,skin)或者Animal.apply(this,[name,eye,skin])实现)

3. 可以实现多继承(call多个父类对象)

缺点:

1. 实例并不是父类的实例,只是子类的实例

2. 只能继承父类的实例属性和方法,不能继承原型属性/方法

3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

推荐指数:★★(缺点3

实例继承

核心:为父类实例添加新特性,作为子类实例返回

function Cat(name) {
    var instance = new Animal();
    instance.name = name || 'Tom';
    return instance;
}
 
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //false
特点:

不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果

缺点:

实例是父类的实例,不是子类的实例

不支持多继承

推荐指数:★★

拷贝继承

function Cat(name) {
    var animal = new Animal();
    for (var p in animal) {
        Cat.prototype[p] = animal[p];
    }
    Cat.prototype.name = name || 'Tom';
}
 
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉!
console.log(cat instanceof Animal); //false
console.log(cat instanceof Cat); //true
特点:

支持多继承

缺点:

1. 效率较低,内存占用高(因为要拷贝父类的属性)

2. 无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

推荐指数:★(缺点1

组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
}
Cat.prototype = new Animal();
 
//Test Code
var cat = new Cat();
console.log(cat.name);                 //Tom
console.log(cat.sleep());              //Tom正在睡觉!
console.log(cat instanceof Animal);    //true
console.log(cat instanceof Cat);       //true
特点:

1. 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法

2. 既是子类的实例,也是父类的实例

3. 不存在引用属性共享问题

4. 可传参

5. 函数可复用

缺点:

调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

推荐指数:★★★★(仅仅多消耗了一点内存)

寄生组合继承

核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

function Cat(name) {
    Animal.call(this);
    this.name = name || 'Tom';
} (function() {
    // 创建一个没有实例方法的类
    var Super = function() {};
    Super.prototype = Animal.prototype;
    //将实例作为子类的原型
    Cat.prototype = new Super();
})();
 
//Test Code
var cat = new Cat();
console.log(cat.name); //Tom
console.log(cat.sleep()); //Tom正在睡觉!
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
特点:

堪称完美

缺点:

实现较为复杂

推荐指数:★★★★(实现复杂,扣掉一颗星)

4.9 附录代码
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    console.log(this.name + '正在睡觉!');
  }
  //实例引用属性
  this.features = [];
}
function Cat(name){
}
Cat.prototype = new Animal();
 
var tom = new Cat('Tom');
var kissy = new Cat('Kissy');
 
console.log(tom.name); // "Animal"
console.log(kissy.name); // "Animal"
console.log(tom.features); // []
console.log(kissy.features); // []
 
tom.name = 'Tom-New Name';
tom.features.push('eat');
 
//针对父类实例值类型成员的更改,不影响
console.log(tom.name); // "Tom-New Name"
console.log(kissy.name); // "Animal"
//针对父类实例引用类型成员的更改,会通过影响其他子类实例
console.log(tom.features); // ['eat']
console.log(kissy.features); // ['eat']
原因分析:

关键点:属性查找过程

执行tom.features.push,首先找tom对象的实例属性(找不到),
那么去原型对象中找,也就是Animal的实例。发现有,那么就直接在这个对象的
features属性中插入值。
在console.log(kissy.features); 的时候。同上,kissy实例上没有,那么去原型上找。
刚好原型上有,就直接返回,但是注意,这个原型对象中features属性值已经变化了。

原型与原型链

JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object ,Function 是JS自带的函数对象。 怎么区分?其实很简单,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。Function Object 也都是通过 New Function()创建的。

构造函数

构造函数模式的目的就是为了创建一个自定义类,并且创建这个类的实例。构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立的,即实例识别。

构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用

原型

在JavaScript中,每当定义一个函数数据类型(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。 让我们用一张图表示构造函数和实例原型之间的关系: 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

原型链

1. __proto__和constructor

每一个对象数据类型(普通的对象、实例、prototype......)也天生自带一个属性__proto__,属性值是当前实例所属类的原型(prototype)。原型对象中有一个属性constructor, 它指向函数对象。

    function Person() {}
    var person = new Person()
    console.log(person.__proto__ === Person.prototype)//true
    console.log(Person.prototype.constructor===Person)//true
    //顺便学习一个ES5的方法,可以获得对象的原型
    console.log(Object.getPrototypeOf(person) === Person.prototype) // true

2.何为原型链

在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。

我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true

    function Person() {}
    Person.prototype.a = 123;
    Person.prototype.sayHello = function () {
      alert("hello");
    };
    var person = new Person()
    console.log(person.a)//123
    console.log(person.hasOwnProperty('a'));//false
    console.log('a'in person)//true

person实例中没有a这个属性,从 person 对象中找不到 a 属性就会从 person 的原型也就是 person.proto ,也就是 Person.prototype中查找,很幸运地得到a的值为123。那假如 person.__proto__中也没有该属性,又该如何查找?

当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层Object为止。Object是JS中所有对象数据类型的基类(最顶层的类)在Object.prototype上没有__proto__这个属性。

console.log(Object.prototype.__proto__ === null) // true

new关键字

假设已经定义了父类Base对象

我们执行如下代码

var obj = new Base(); 这样代码的结果是什么,我们在Javascript引擎中看到的对象模型是:

new操作符具体干了什么呢?其实很简单,就干了三件事情。

var obj = {}; 
obj.__proto__ = Base.prototype; 
Base.call(obj); 

第一行,我们创建了一个空对象obj

第二行,我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象

第三行,我们将Base函数对象的this指针替换成obj,然后再调用Base函数

注意:new的过程会执行构造函数Base() 再对空对象进行构造

如何实现图片懒加载

图片懒加载的原理很简单,就是我们先设置图片的data-set属性(当然也可以是其他任意的,只要不会发送http请求就行了,作用就是为了存取值)值为其图片路径,由于不是src,所以不会发送http请求。 然后我们计算出页面scrollTop的高度和浏览器的高度之和, 如果图片举例页面顶端的坐标Y(相对于整个页面,而不是浏览器窗口)小于前两者之和,就说明图片就要显示出来了(合适的时机,当然也可以是其他情况),这时候我们再将 data-set 属性替换为 src 属性即可。

同源与跨域

同源

同源策略: 同协议、同域名、同端口

同源策略目的:为了保证用户信息的安全,不被恶意的网站窃取数据

如果是非同源,共有三种行为受到限制:

1、ajax请求

2、dom无法获取

3、cookie、localStorage、indexDB无法读取

虽然这些限制是合理且必需的,但是有时候一些合理地用途也会受到限制

跨域

常见解决跨域方案:

1、webpack的proxyTable方案

在一般项目中都会有webpack对应的开发环境的配置文件:webpack.dev.js,在配置项中加入ProxyTable的配置项即可:

proxy: {
          '/api': {
          changeOrigin: true,
          target: 'http://******.*****.com',
    }
}

如果前后端前缀不匹配或者后端前缀不统一的情况,可以增加pathRewrite属性来统一:

proxy: {
            '/EntryApp': {
              changeOrigin: true,
              target: 'http://******.*******.com',
              pathRewrite: {"^/EntryApp": "/EntryApp"}
        },
    }
2. cors

该方案一般由由后端同学配置处理

3. nginx配置

Nginx是一个免费的,开源的高性能的HTTP和反向代理服务器。

nginx.conf是主配置文件,有若干个部分组成,每一部分都用{}区分。主要包括:

  • main:nginx的全局配置,对全局生效
  • events:影响nginx服务器或与用户的网络连接
  • http:可以嵌套多个server,配置代理,缓存,日志等
  • server:配置虚拟主机的相关参数,一个http可以有多个server

nginx解决跨域的基本方法是在sever中配置proxy_pass:

// 前端服务的域名为 fe.**.com
// 后端服务的域名为 dev.**.com
server {
    listen: 80,
    server_name: fe.**.com,
    location / {
         proxy_pass dev.**.com
    }
}

根据实际需求,还可以添加一些其他的指令,比如:

  • proxy_connect_timeout:nginx从接受请求至连接到上游服务器的最长等待时间
  • proxy_cookie_domain:替代上游服务器的set_cookie头的domain属性
  • proxy_cookie_path:替代上游服务器的set_cookie头的path
  • proxy_set_header:重写发送到上游服务器头的内容,也可以通过将某个头部的值设置为空字符串,而不发送某个头部的方式发放实现
4.jsonp

基本思路:网页通过添加一个 <script> 元素,向服务器请求JSON数据,并且该请求的查询字符串有一个callback 参数,用来指定回调函数的名字,这种做法不受同源政策限制; 服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

提问: JSONP 为什么不支持 POST?

  • 因为 JSONP 是通过动态创建 script标签实现的
  • 创建 <script> 只能发送 get 请求

cookie 和 session

会话跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

cookie相关参数:

  • Name:Cookie 的名称
  • Value:Cookie 的值
  • Domain:Cookie 的域

域名只能为一个,不能有多个。但是可以设置域名等级,例如,domain 为 A.study.com,那么只有A.study.com能获取到这个 Cookie;domain 为 .study.com,那么 A.study.com 以及B.study.com 都能获取到这个 Cookie。

  • Path:路径

默认为/,你也可以设置为/login,那么/login可以获取到/login以及/下的 Cookie,而/只能获取到/下的 Cookie。

  • Expires/Max-age:Cookie的过期时间

默认为一次会话,关闭浏览器一会后就消失,也可以自己设置。

  • Size:Cookie 的大小
  • HttpOnly:JS脚本是否可以获取(默认可以获取,为false)

若设置为 true,JS 脚本将无法获取 Cookie,能有效的防止XSS攻击。

  • Secure:JS脚本是否可以获取(默认可以获取,为false)

若为 true,那么 Cookie 只能在HTTPS连接中传输;若为 false,HTTP、HTTPS连接都行。

  • SameSite:限制第三方cookie

有3个值:Strict/Lax/None。chrome51新增,chrome80+强制执行。

  1. Strict: 仅允许发送同站点请求的的cookie;
  2. Lax: 仅get请求跨站(chrome 80+之后的默认值)。
  3. None: 任意发送cookie,但是chrome 80+之后,设置为None,需要同时设置Secure,意味着网站必须采用https

Cookie 与 webStorage 的区别

CookiesessionStoragelocalStorage
有效期默认会话(关闭浏览器后消失,也可设置时间)会话(关闭浏览器一会后消失)始终有效
作用域同域(端口号和协议可以不同,后面会说明)当前页面同源
大小4kb 左右(各浏览器不同)5mb(各浏览器不同)5mb(各浏览器不同)
兼容性用户可禁用ie8+ie8+

session

session是一种服务度机制,类似散列表结构来存储用户数据
浏览器第一次向客户端发送请求时,服务器会自动生成一个session和sessionid
sessionid唯一标识这个session
服务器响应时把sessionid发送给浏览器
浏览器第二次向服务器发送请求时就会携带这个sessionid
服务器通过这个id找到对应的session获取用户数据

浅拷贝与深拷贝

原生ajax 封装

<script type="text/javascript">
   function ajax (options) {
   	// 存储的是默认值
   	var defaults = {
   		type: 'get',
   		url: '',
   		data: {},
   		header: {
   			'Content-Type': 'application/x-www-form-urlencoded'
   			},
   		success: function () {},
   		error: function () {}
   	};
   	// 使用options对象中的属性覆盖defaults对象中的属性
   	Object.assign(defaults, options);
   	// 创建ajax对象
   	var xhr = new XMLHttpRequest();
   	// 拼接请求参数的变量
   	var params = '';
   	// 循环用户传递进来的对象格式参数
   	for (var attr in defaults.data) {
   			// 将参数转换为字符串格式
   			params += attr + '=' + defaults.data[attr] + '&';
   		}
   	// 将参数最后面的&截取掉 
   	// 将截取的结果重新赋值给params变量
   	params = params.substr(0, params.length - 1);
   	// 判断请求方式
   	if (defaults.type == 'get') {
   			defaults.url = defaults.url + '?' + params;
   		}
   	// 配置ajax对象
   	xhr.open(defaults.type, defaults.url);
   	// 如果请求方式为post
   	if (defaults.type == 'post') {
   			// 用户希望的向服务器端传递的请求参数的类型
   			var contentType = defaults.header['Content-Type']
   			// 设置请求参数格式的类型
   			xhr.setRequestHeader('Content-Type', contentType);
   			// 判断用户希望的请求参数格式的类型
   			// 如果类型为json
   			if (contentType == 'application/json') {
   				// 向服务器端传递json数据格式的参数
   				xhr.send(JSON.stringify(defaults.data))
   			}else {
   				// 向服务器端传递普通类型的请求参数
   				xhr.send(params);
   			}

   		}else {
   		// 发送请求
   		xhr.send();
   	}
   	// 监听xhr对象下面的onload事件
   	// 当xhr对象接收完响应数据后触发
   	xhr.onload = function () {
   		// xhr.getResponseHeader()
   		// 获取响应头中的数据
   		var contentType = xhr.getResponseHeader('Content-Type');
   		// 服务器端返回的数据
   		var responseText = xhr.responseText;
   		// 如果响应类型中包含applicaition/json
   		if (contentType.includes('application/json')) {
   			// 将json字符串转换为json对象
   			responseText = JSON.parse(responseText)
   		}
   		// 当http状态码等于200的时候
   		if (xhr.status == 200) {
   			// 请求成功 调用处理成功情况的函数
   			defaults.success(responseText, xhr);
   		}else {
   			// 请求失败 调用处理失败情况的函数
   			defaults.error(responseText, xhr);
   		}
   	}
   }
   ajax({
   	type: 'post',
   	// 请求地址
   	url: 'http://localhost:3000/responseData',
   	success: function (data) {
   		console.log('这里是success函数');
   		console.log(data)
   	}
   })
</script>

http Content-Type 知多少

参考 【潇湘待雨】的文章: juejin.cn/post/684490…

http与https

react面试题

参考文章: juejin.cn/post/684490…

vue 面试题

参考文章:juejin.cn/post/684490…

JS线程、Event Loop、事件循环、任务队列、宏任务

参考文章: juejin.cn/post/684490…