乐趣区

关于前端:有哪些前端面试题是面试官必考的

Iterator 迭代器

Iterator(迭代器)是一种接口,也能够说是一种标准。为各种不同的数据结构提供对立的拜访机制。任何数据结构只有部署 Iterator 接口,就能够实现遍历操作(即顺次解决该数据结构的所有成员)。

Iterator 语法:

const obj = {[Symbol.iterator]:function(){}
}

[Symbol.iterator] 属性名是固定的写法,只有领有了该属性的对象,就可能用迭代器的形式进行遍历。

  • 迭代器的遍历办法是首先取得一个迭代器的指针,初始时该指针指向第一条数据之前,接着通过调用 next 办法,扭转指针的指向,让其指向下一条数据
  • 每一次的 next 都会返回一个对象,该对象有两个属性

    • value 代表想要获取的数据
    • done 布尔值,false 示意以后指针指向的数据有值,true 示意遍历曾经完结

Iterator 的作用有三个:

  • 创立一个指针对象,指向以后数据结构的起始地位。也就是说,遍历器对象实质上,就是一个指针对象。
  • 第一次调用指针对象的 next 办法,能够将指针指向数据结构的第一个成员。
  • 第二次调用指针对象的 next 办法,指针就指向数据结构的第二个成员。
  • 一直调用指针对象的 next 办法,直到它指向数据结构的完结地位。

每一次调用 next 办法,都会返回数据结构的以后成员的信息。具体来说,就是返回一个蕴含 value 和 done 两个属性的对象。其中,value 属性是以后成员的值,done 属性是一个布尔值,示意遍历是否完结。

let arr = [{num:1},2,3]
let it = arr[Symbol.iterator]() // 获取数组中的迭代器
console.log(it.next())  // {value: Object { num: 1}, done: false }
console.log(it.next())  // {value: 2, done: false}
console.log(it.next())  // {value: 3, done: false}
console.log(it.next())  // {value: undefined, done: true}

对象没有布局 Iterator 接口,无奈应用for of 遍历。上面使得对象具备 Iterator 接口

  • 一个数据结构只有有 Symbol.iterator 属性,就能够认为是“可遍历的”
  • 原型部署了 Iterator 接口的数据结构有三种,具体蕴含四种,别离是数组,相似数组的对象,Set 和 Map 构造

为什么对象(Object)没有部署 Iterator 接口呢?

  • 一是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,须要开发者手动指定。然而遍历遍历器是一种线性解决,对于非线性的数据结构,部署遍历器接口,就等于要部署一种线性转换
  • 对对象部署 Iterator 接口并不是很必要,因为 Map 补救了它的缺点,又正好有 Iteraotr 接口
let obj = {
    id: '123',
    name: '张三',
    age: 18,
    gender: '男',
    hobbie: '睡觉'
}

obj[Symbol.iterator] = function () {let keyArr = Object.keys(obj)
    let index = 0
    return {next() {
            return index < keyArr.length ? {
                value: {key: keyArr[index],
                    val: obj[keyArr[index++]]
                }
            } : {done: true}
        }
    }
}

for (let key of obj) {console.log(key)
}

Generator

GeneratorES6中新增的语法,和 Promise 一样,都能够用来异步编程。Generator 函数能够说是 Iterator 接口的具体实现形式。Generator 最大的特点就是能够管制函数的执行。

  • function* 用来申明一个函数是生成器函数,它比一般的函数申明多了一个 *,* 的地位比拟随便能够挨着 function 关键字,也能够挨着函数名
  • yield 产出的意思,这个关键字只能呈现在生成器函数体内,然而生成器中也能够没有yield 关键字,函数遇到 yield 的时候会暂停,并把 yield 前面的表达式后果抛出去
  • next作用是将代码的控制权交还给生成器函数
function *foo(x) {let y = 2 * (yield (x + 1))
  let z = yield (y / 3)
  return (x + y + z)
}
let it = foo(5)
console.log(it.next())   // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}

下面这个示例就是一个 Generator 函数,咱们来剖析其执行过程:

  • 首先 Generator 函数调用时它会返回一个迭代器
  • 当执行第一次 next 时,传参会被疏忽,并且函数暂停在 yield (x + 1) 处,所以返回 5 + 1 = 6
  • 当执行第二次 next 时,传入的参数等于上一个 yield 的返回值,如果你不传参,yield 永远返回 undefined。此时 let y = 2 12,所以第二个 yield 等于 2 12 / 3 = 8
  • 当执行第三次 next 时,传入的参数会传递给 z,所以 z = 13, x = 5, y = 24,相加等于 42

yield理论就是暂缓执行的标示,每执行一次 next(),相当于指针挪动到下一个yield 地位

总结一下 Generator 函数是 ES6 提供的一种异步编程解决方案。通过 yield 标识位和 next() 办法调用,实现函数的分段执行

遍历器对象生成函数,最大的特点是能够交出函数的执行权

  • function 关键字与函数名之间有一个星号;
  • 函数体外部应用 yield表达式,定义不同的外部状态;
  • next指针移向下一个状态

这里你能够说说 Generator的异步编程,以及它的语法糖 asyncawiat,传统的异步编程。ES6 之前,异步编程大抵如下

  • 回调函数
  • 事件监听
  • 公布 / 订阅

传统异步编程计划之一:协程,多个线程相互合作,实现异步工作。

// 应用 * 示意这是一个 Generator 函数
// 外部能够通过 yield 暂停代码
// 通过调用 next 复原执行
function* test() {
  let a = 1 + 2;
  yield 2;
  yield 3;
}
let b = test();
console.log(b.next()); // >  {value: 2, done: false}
console.log(b.next()); // >  {value: 3, done: false}
console.log(b.next()); // >  {value: undefined, done: true}

从以上代码能够发现,加上 *的函数执行后领有了 next 函数,也就是说函数执行后返回了一个对象。每次调用 next 函数能够继续执行被暂停的代码。以下是 Generator 函数的简略实现

// cb 也就是编译过的 test 函数
function generator(cb) {return (function() {
    var object = {
      next: 0,
      stop: function() {}
    };

    return {next: function() {var ret = cb(object);
        if (ret === undefined) return {value: undefined, done: true};
        return {
          value: ret,
          done: false
        };
      }
    };
  })();}
// 如果你应用 babel 编译后能够发现 test 函数变成了这样
function test() {
  var a;
  return generator(function(_context) {while (1) {switch ((_context.prev = _context.next)) {
        // 能够发现通过 yield 将代码宰割成几块
        // 每次执行 next 函数就执行一块代码
        // 并且表明下次须要执行哪块代码
        case 0:
          a = 1 + 2;
          _context.next = 4;
          return 2;
        case 4:
          _context.next = 6;
          return 3;
        // 执行结束
        case 6:
        case "end":
          return _context.stop();}
    }
  });
}

作用域

  • 作用域:作用域是定义变量的区域,它有一套拜访变量的规定,这套规定来治理浏览器引擎如何在以后作用域以及嵌套的作用域中依据变量(标识符)进行变量查找
  • 作用域链:作用域链的作用是保障对执行环境有权拜访的所有变量和函数的有序拜访,通过作用域链,咱们能够拜访到外层环境的变量和 函数。

作用域链的实质上是一个指向变量对象的指针列表。变量对象是一个蕴含了执行环境中所有变量和函数的对象。作用域链的前 端始终都是以后执行上下文的变量对象。全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最初一个对象。

  • 当咱们查找一个变量时,如果以后执行环境中没有找到,咱们能够沿着作用域链向后查找
  • 作用域链的创立过程跟执行上下文的建设无关 ….

作用域能够了解为变量的可拜访性,总共分为三种类型,别离为:

  • 全局作用域
  • 函数作用域
  • 块级作用域,ES6 中的 letconst 就能够产生该作用域

其实看完后面的闭包、this 这部分外部的话,应该根本能理解作用域的一些利用。

一旦咱们将这些作用域嵌套起来,就变成了另外一个重要的知识点「作用域链」,也就是 JS 到底是如何拜访须要的变量或者函数的。

  • 首先作用域链是在定义时就被确定下来的,和箭头函数里的 this 一样,后续不会扭转,JS 会一层层往上寻找须要的内容。
  • 其实作用域链这个货色咱们在闭包小结中曾经看到过它的实体了:[[Scopes]]

图中的 [[Scopes]] 是个数组,作用域的一层层往上寻找就等同于遍历 [[Scopes]]

1. 全局作用域

全局变量是挂载在 window 对象下的变量,所以在网页中的任何地位你都能够应用并且拜访到这个全局变量

var globalName = 'global';
function getName() {console.log(globalName) // global
  var name = 'inner'
  console.log(name) // inner
} 
getName();
console.log(name); // 
console.log(globalName); //global
function setName(){vName = 'setName';}
setName();
console.log(vName); // setName
  • 从这段代码中咱们能够看到,globalName 这个变量无论在什么中央都是能够被拜访到的,所以它就是全局变量。而在 getName 函数中作为局部变量的 name 变量是不具备这种能力的
  • 当然全局作用域有相应的毛病,咱们定义很多全局变量的时候,会容易引起变量命名的抵触,所以在定义变量的时候应该留神作用域的问题。

2. 函数作用域

函数中定义的变量叫作函数变量,这个时候只能在函数外部能力拜访到它,所以它的作用域也就是函数的外部,称为函数作用域

function getName () {
  var name = 'inner';
  console.log(name); //inner
}
getName();
console.log(name);

除了这个函数外部,其余中央都是不能拜访到它的。同时,当这个函数被执行完之后,这个局部变量也相应会被销毁。所以你会看到在 getName 函数里面的 name 是拜访不到的

3. 块级作用域

ES6 中新增了块级作用域,最间接的体现就是新增的 let 关键词,应用 let 关键词定义的变量只能在块级作用域中被拜访,有“暂时性死区”的特点,也就是说这个变量在定义之前是不能被应用的。

在 JS 编码过程中 if 语句 for 语句前面 {...} 这外面所包含的,就是 块级作用域

console.log(a) //a is not defined
if(true){
  let a = '123';console.log(a);// 123
}
console.log(a) //a is not defined

从这段代码能够看出,变量 a 是在 if 语句 {...} 中由 let 关键词 进行定义的变量,所以它的作用域是 if 语句括号中的那局部,而在里面进行拜访 a 变量是会报错的,因为这里不是它的作用域。所以在 if 代码块的前后输入 a 这个变量的后果,控制台会显示 a 并没有定义

HTTPS 的特点

HTTPS 的 长处 如下:

  • 应用 HTTPS 协定能够认证用户和服务器,确保数据发送到正确的客户端和服务器;
  • 应用 HTTPS 协定能够进行加密传输、身份认证,通信更加平安,避免数据在传输过程中被窃取、批改,确保数据安全性;
  • HTTPS 是现行架构下最平安的解决方案,尽管不是相对的平安,然而大幅减少了中间人攻打的老本;

HTTPS 的 毛病 如下:

  • HTTPS 须要做服务器和客户端单方的加密个解密解决,消耗更多服务器资源,过程简单;
  • HTTPS 协定握手阶段比拟费时,减少页面的加载工夫;
  • SSL 证书是免费的,性能越弱小的证书费用越高;
  • HTTPS 连贯服务器端资源占用高很多,反对访客稍多的网站须要投入更大的老本;
  • SSL 证书须要绑定 IP,不能再同一个 IP 上绑定多个域名。

执行上下文

当执行 JS 代码时,会产生三种执行上下文

  • 全局执行上下文
  • 函数执行上下文
  • eval 执行上下文

每个执行上下文中都有三个重要的属性

  • 变量对象(VO),蕴含变量、函数申明和函数的形参,该属性只能在全局上下文中拜访
  • 作用域链(JS 采纳词法作用域,也就是说变量的作用域是在定义时就决定了)
  • this
var a = 10
function foo(i) {var b = 20}
foo()

对于上述代码,执行栈中有两个上下文:全局上下文和函数 foo 上下文。

stack = [
    globalContext,
    fooContext
]

对于全局上下文来说,VO大略是这样的

globalContext.VO === globe
globalContext.VO = {
    a: undefined,
    foo: <Function>,
}

对于函数 foo 来说,VO 不能拜访,只能拜访到流动对象(AO

fooContext.VO === foo.AO
fooContext.AO {
    i: undefined,
    b: undefined,
    arguments: <>
}
// arguments 是函数独有的对象(箭头函数没有)
// 该对象是一个伪数组,有 `length` 属性且能够通过下标拜访元素
// 该对象中的 `callee` 属性代表函数自身
// `caller` 属性代表函数的调用者

对于作用域链,能够把它了解成蕴含本身变量对象和下级变量对象的列表,通过 [[Scope]]属性查找下级变量

fooContext.[[Scope]] = [globalContext.VO]
fooContext.Scope = fooContext.[[Scope]] + fooContext.VO
fooContext.Scope = [
    fooContext.VO,
    globalContext.VO
]

接下来让咱们看一个陈词滥调的例子,var

b() // call b
console.log(a) // undefined

var a = 'Hello world'

function b() {console.log('call b')
}

想必以上的输入大家必定都曾经明确了,这是因为函数和变量晋升的起因。通常晋升的解释是说将申明的代码移动到了顶部,这其实没有什么谬误,便于大家了解。然而更精确的解释应该是:在生成执行上下文时,会有两个阶段。第一个阶段是创立的阶段(具体步骤是创立 VO),JS 解释器会找出须要晋升的变量和函数,并且给他们提前在内存中开拓好空间,函数的话会将整个函数存入内存中,变量只申明并且赋值为 undefined,所以在第二个阶段,也就是代码执行阶段,咱们能够间接提前应用。

  • 在晋升的过程中,雷同的函数会笼罩上一个函数,并且函数优先于变量晋升
b() // call b second

function b() {console.log('call b fist')
}
function b() {console.log('call b second')
}
var b = 'Hello world'

var会产生很多谬误,所以在 ES6中引入了 letlet不能在申明前应用,然而这并不是常说的 let 不会晋升,let 晋升了申明但没有赋值,因为长期死区导致了并不能在申明前应用。

  • 对于非匿名的立刻执行函数须要留神以下一点
var foo = 1
(function foo() {
    foo = 10
    console.log(foo)
}()) // -> ƒ foo() { foo = 10 ; console.log(foo) }

因为当 JS 解释器在遇到非匿名的立刻执行函数时,会创立一个辅助的特定对象,而后将函数名称作为这个对象的属性,因而函数外部才能够拜访到 foo,然而这个值又是只读的,所以对它的赋值并不失效,所以打印的后果还是这个函数,并且内部的值也没有产生更改。

specialObject = {};

Scope = specialObject + Scope;

foo = new FunctionExpression;
foo.[[Scope]] = Scope;
specialObject.foo = foo; // {DontDelete}, {ReadOnly}

delete Scope[0]; // remove specialObject from the front of scope chain

总结

执行上下文能够简略了解为一个对象:

它蕴含三个局部:

  • 变量对象(VO)
  • 作用域链(词法作用域)
  • this指向

它的类型:

  • 全局执行上下文
  • 函数执行上下文
  • eval执行上下文

代码执行过程:

  • 创立 全局上下文 (global EC)
  • 全局执行上下文 (caller) 逐行 自上而下 执行。遇到函数时,函数执行上下文 (callee) 被 push 到执行栈顶层
  • 函数执行上下文被激活,成为 active EC, 开始执行函数中的代码,caller 被挂起
  • 函数执行完后,calleepop 移除出执行栈,控制权交还全局上下文 (caller),继续执行

HTTP 状态码

状态码的类别:

类别 起因 形容
1xx Informational(信息性状态码) 承受的申请正在解决
2xx Success(胜利状态码) 申请失常处理完毕
3xx Redirection(重定向状态码) 须要进行附加操作一实现申请
4xx Client Error (客户端谬误状态码) 服务器无奈解决申请
5xx Server Error(服务器谬误状态码) 服务器解决申请出错

1. 2XX (Success 胜利状态码)

状态码 2XX 示意申请被失常解决了。

(1)200 OK

200 OK 示意客户端发来的申请被服务器端失常解决了。

(2)204 No Content

该状态码示意客户端发送的申请曾经在服务器端失常解决了,然而没有返回的内容,响应报文中不蕴含实体的主体局部。个别在只须要从客户端往服务器端发送信息,而服务器端不须要往客户端发送内容时应用。

(3)206 Partial Content

该状态码示意客户端进行了范畴申请,而服务器端执行了这部分的 GET 申请。响应报文中蕴含由 Content-Range 指定范畴的实体内容。

2. 3XX (Redirection 重定向状态码)

3XX 响应结果表明浏览器须要执行某些非凡的解决以正确处理申请。

(1)301 Moved Permanently

永恒重定向。 该状态码示意申请的资源曾经被调配了新的 URI,当前应应用资源指定的 URI。新的 URI 会在 HTTP 响应头中的 Location 首部字段指定。若用户曾经把原来的 URI 保留为书签,此时会依照 Location 中新的 URI 从新保留该书签。同时,搜索引擎在抓取新内容的同时也将旧的网址替换为重定向之后的网址。

应用场景:

  • 当咱们想换个域名,旧的域名不再应用时,用户拜访旧域名时用 301 就重定向到新的域名。其实也是通知搜索引擎收录的域名须要对新的域名进行收录。
  • 在搜索引擎的搜寻后果中呈现了不带 www 的域名,而带 www 的域名却没有收录,这个时候能够用 301 重定向来通知搜索引擎咱们指标的域名是哪一个。
(2)302 Found

长期重定向。 该状态码示意申请的资源被调配到了新的 URI,心愿用户(本次)能应用新的 URI 拜访资源。和 301 Moved Permanently 状态码类似,然而 302 代表的资源不是被永恒重定向,只是长期性质的。也就是说已挪动的资源对应的 URI 未来还有可能产生扭转。若用户把 URI 保留成书签,但不会像 301 状态码呈现时那样去更新书签,而是仍旧保留返回 302 状态码的页面对应的 URI。同时,搜索引擎会抓取新的内容而保留旧的网址。因为服务器返回 302 代码,搜索引擎认为新的网址只是临时的。

应用场景:

  • 当咱们在做流动时,登录到首页主动重定向,进入流动页面。
  • 未登陆的用户拜访用户核心重定向到登录页面。
  • 拜访 404 页面从新定向到首页。
(3)303 See Other

该状态码示意因为申请对应的资源存在着另一个 URI,应应用 GET 办法定向获取申请的资源。
303 状态码和 302 Found 状态码有着类似的性能,然而 303 状态码明确示意客户端该当采纳 GET 办法获取资源。

303 状态码通常作为 PUT 或 POST 操作的返回后果,它示意重定向链接指向的不是新上传的资源,而是另外一个页面,比方音讯确认页面或上传进度页面。而申请重定向页面的办法要总是应用 GET。

留神:

  • 当 301、302、303 响应状态码返回时,简直所有的浏览器都会把 POST 改成 GET,并删除申请报文内的主体,之后申请会再次主动发送。
  • 301、302 规范是禁止将 POST 办法变成 GET 办法的,但理论大家都会这么做。
(4)304 Not Modified

浏览器缓存相干。 该状态码示意客户端发送附带条件的申请时,服务器端容许申请拜访资源,但未满足条件的状况。304 状态码返回时,不蕴含任何响应的主体局部。304 尽管被划分在 3XX 类别中,然而和重定向没有关系。

带条件的申请(Http 条件申请):应用 Get 办法 申请,申请报文中蕴含(if-matchif-none-matchif-modified-sinceif-unmodified-sinceif-range)中任意首部。

状态码 304 并不是一种谬误,而是通知客户端有缓存,间接应用缓存中的数据。返回页面的只有头部信息,是没有内容局部的,这样在肯定水平上进步了网页的性能。

(5)307 Temporary Redirect

307 示意长期重定向。 该状态码与 302 Found 有着雷同含意,只管 302 规范禁止 POST 变成 GET,然而理论应用时还是这样做了。

307 会恪守浏览器规范,不会从 POST 变成 GET。然而对于解决申请的行为时,不同浏览器还是会呈现不同的状况。标准要求浏览器持续向 Location 的地址 POST 内容。标准要求浏览器持续向 Location 的地址 POST 内容。

3. 4XX (Client Error 客户端谬误状态码)

4XX 的响应结果表明客户端是产生谬误的起因所在。

(1)400 Bad Request

该状态码示意申请报文中存在语法错误。当谬误产生时,需批改申请的内容后再次发送申请。另外,浏览器会像 200 OK 一样看待该状态码。

(2)401 Unauthorized

该状态码示意发送的申请须要有通过 HTTP 认证 (BASIC 认证、DIGEST 认证) 的认证信息。若之前已进行过一次申请,则示意用户认证失败

返回含有 401 的响应必须蕴含一个实用于被申请资源的 WWW-Authenticate 首部用以质询 (challenge) 用户信息。当浏览器首次接管到 401 响应,会弹出认证用的对话窗口。

以下状况会呈现 401:

  • 401.1 – 登录失败。
  • 401.2 – 服务器配置导致登录失败。
  • 401.3 – 因为 ACL 对资源的限度而未取得受权。
  • 401.4 – 筛选器受权失败。
  • 401.5 – ISAPI/CGI 应用程序受权失败。
  • 401.7 – 拜访被 Web 服务器上的 URL 受权策略回绝。这个错误代码为 IIS 6.0 所专用。
(3)403 Forbidden

该状态码表明申请资源的拜访被服务器回绝了,服务器端没有必要给出具体理由,然而能够在响应报文实体的主体中进行阐明。进入该状态后,不能再持续进行验证。该拜访是永恒禁止的,并且与应用逻辑密切相关。

IIS 定义了许多不同的 403 谬误,它们指明更为具体的谬误起因:

  • 403.1 – 执行拜访被禁止。
  • 403.2 – 读拜访被禁止。
  • 403.3 – 写访问被禁止。
  • 403.4 – 要求 SSL。
  • 403.5 – 要求 SSL 128。
  • 403.6 – IP 地址被回绝。
  • 403.7 – 要求客户端证书。
  • 403.8 – 站点拜访被回绝。
  • 403.9 – 用户数过多。
  • 403.10 – 配置有效。
  • 403.11 – 明码更改。
  • 403.12 – 回绝拜访映射表。
  • 403.13 – 客户端证书被撤消。
  • 403.14 – 回绝目录列表。
  • 403.15 – 超出客户端拜访许可。
  • 403.16 – 客户端证书不受信赖或有效。
  • 403.17 – 客户端证书已过期或尚未失效
  • 403.18 – 在以后的应用程序池中不能执行所申请的 URL。这个错误代码为 IIS 6.0 所专用。
  • 403.19 – 不能为这个应用程序池中的客户端执行 CGI。这个错误代码为 IIS 6.0 所专用。
  • 403.20 – Passport 登录失败。这个错误代码为 IIS 6.0 所专用。
(4)404 Not Found

该状态码表明服务器上无奈找到申请的资源。除此之外,也能够在服务器端拒绝请求且不想阐明理由时应用。
以下状况会呈现 404:

  • 404.0 -(无)– 没有找到文件或目录。
  • 404.1 – 无奈在所申请的端口上拜访 Web 站点。
  • 404.2 – Web 服务扩大锁定策略阻止本申请。
  • 404.3 – MIME 映射策略阻止本申请。
(5)405 Method Not Allowed

该状态码示意客户端申请的办法尽管能被服务器辨认,然而服务器禁止应用该办法。GET 和 HEAD 办法,服务器应该总是容许客户端进行拜访。客户端能够通过 OPTIONS 办法(预检)来查看服务器容许的拜访办法, 如下

Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE

4. 5XX (Server Error 服务器谬误状态码)

5XX 的响应结果表明服务器自身产生谬误.

(1)500 Internal Server Error

该状态码表明服务器端在执行申请时产生了谬误。也有可能是 Web 利用存在的 bug 或某些长期的故障。

(2)502 Bad Gateway

该状态码表明表演网关或代理角色的服务器,从上游服务器中接管到的响应是有效的。留神,502 谬误通常不是客户端可能修复的,而是须要由途经的 Web 服务器或者代理服务器对其进行修复。以下状况会呈现 502:

  • 502.1 – CGI(通用网关接口)应用程序超时。
  • 502.2 – CGI(通用网关接口)应用程序出错。
(3)503 Service Unavailable

该状态码表明服务器临时处于超负载或正在进行停机保护,当初无奈解决申请。如果当时得悉解除以上情况须要的工夫,最好写入 RetryAfter 首部字段再返回给客户端。

应用场景:

  • 服务器停机保护时,被动用 503 响应申请;
  • nginx 设置限速,超过限速,会返回 503。
(4)504 Gateway Timeout

该状态码示意网关或者代理的服务器无奈在规定的工夫内取得想要的响应。他是 HTTP 1.1 中新退出的。

应用场景:代码执行工夫超时,或者产生了死循环。

5. 总结

(1)2XX 胜利

  • 200 OK,示意从客户端发来的申请在服务器端被正确处理
  • 204 No content,示意申请胜利,但响应报文不含实体的主体局部
  • 205 Reset Content,示意申请胜利,但响应报文不含实体的主体局部,然而与 204 响应不同在于要求申请方重置内容
  • 206 Partial Content,进行范畴申请

(2)3XX 重定向

  • 301 moved permanently,永久性重定向,示意资源已被调配了新的 URL
  • 302 found,临时性重定向,示意资源长期被调配了新的 URL
  • 303 see other,示意资源存在着另一个 URL,应应用 GET 办法获取资源
  • 304 not modified,示意服务器容许拜访资源,但因产生申请未满足条件的状况
  • 307 temporary redirect,长期重定向,和 302 含意相似,然而冀望客户端放弃申请办法不变向新的地址发出请求

(3)4XX 客户端谬误

  • 400 bad request,申请报文存在语法错误
  • 401 unauthorized,示意发送的申请须要有通过 HTTP 认证的认证信息
  • 403 forbidden,示意对申请资源的拜访被服务器回绝
  • 404 not found,示意在服务器上没有找到申请的资源

(4)5XX 服务器谬误

  • 500 internal sever error,示意服务器端在执行申请时产生了谬误
  • 501 Not Implemented,示意服务器不反对以后申请所须要的某个性能
  • 503 service unavailable,表明服务器临时处于超负载或正在停机保护,无奈解决申请

参考 前端进阶面试题具体解答

介绍 plugin

插件零碎是 Webpack 胜利的一个关键性因素。在编译的整个生命周期中,Webpack 会触发许多事件钩子,Plugin 能够监听这些事件,依据需要在相应的工夫点对打包内容进行定向的批改。

一个最简略的 plugin 是这样的:

class Plugin{
      // 注册插件时,会调用 apply 办法
      // apply 办法接管 compiler 对象
      // 通过 compiler 上提供的 Api,能够对事件进行监听,执行相应的操作
      apply(compiler){
          // compilation 是监听每次编译循环
          // 每次文件变动,都会生成新的 compilation 对象并触发该事件
        compiler.plugin('compilation',function(compilation) {})
      }
}

注册插件:

// webpack.config.js
module.export = {
    plugins:[new Plugin(options),
    ]
}

事件流机制:

Webpack 就像工厂中的一条产品流水线。原材料通过 Loader 与 Plugin 的一道道解决,最初输入后果。

  • 通过链式调用,按程序串起一个个 Loader;
  • 通过事件流机制,让 Plugin 能够插入到整个生产过程中的每个步骤中;

Webpack 事件流编程范式的外围是根底类 Tapable,是一种 观察者模式 的实现事件的订阅与播送:

const {SyncHook} = require("tapable")

const hook = new SyncHook(['arg'])

// 订阅
hook.tap('event', (arg) => {
    // 'event-hook'
    console.log(arg)
})

// 播送
hook.call('event-hook')

Webpack 中两个最重要的类 CompilerCompilation 便是继承于 Tapable,也领有这样的事件流机制。

  • Compiler : 能够简略的了解为 Webpack 实例,它蕴含了以后 Webpack 中的所有配置信息,如 options,loaders, plugins 等信息,全局惟一,只在启动时实现初始化创立,随着生命周期逐个传递;
  • Compilation: 能够称为 编译实例。当监听到文件产生扭转时,Webpack 会创立一个新的 Compilation 对象,开始一次新的编译。它蕴含了以后的输出资源,输入资源,变动的文件等,同时通过它提供的 api,能够监听每次编译过程中触发的事件钩子;
  • 区别:

    • Compiler 全局惟一,且从启动生存到完结;
    • Compilation对应每次编译,每轮编译循环均会从新创立;
  • 罕用 Plugin:

    • UglifyJsPlugin: 压缩、混同代码;
    • CommonsChunkPlugin: 代码宰割;
    • ProvidePlugin: 主动加载模块;
    • html-webpack-plugin: 加载 html 文件,并引入 css / js 文件;
    • extract-text-webpack-plugin / mini-css-extract-plugin: 抽离款式,生成 css 文件;DefinePlugin: 定义全局变量;
    • optimize-css-assets-webpack-plugin: CSS 代码去重;
    • webpack-bundle-analyzer: 代码剖析;
    • compression-webpack-plugin: 应用 gzip 压缩 js 和 css;
    • happypack: 应用多过程,减速代码构建;
    • EnvironmentPlugin: 定义环境变量;
  • 调用插件 apply 函数传入 compiler 对象
  • 通过 compiler 对象监听事件

loader 和 plugin 有什么区别?

webapck 默认只能打包 JS 和 JOSN 模块,要打包其它模块,须要借助 loader,loader 就能够让模块中的内容转化成 webpack 或其它 laoder 能够辨认的内容。

  • loader就是模块转换化,或叫加载器。不同的文件,须要不同的 loader 来解决。
  • plugin是插件,能够参加到整个 webpack 打包的流程中,不同的插件,在适合的机会,能够做不同的事件。

webpack 中都有哪些插件,这些插件有什么作用?

  • html-webpack-plugin 主动创立一个 HTML 文件,并把打包好的 JS 插入到 HTML 文件中
  • clean-webpack-plugin 在每一次打包之前,删除整个输入文件夹下所有的内容
  • mini-css-extrcat-plugin 抽离 CSS 代码,放到一个独自的文件中
  • optimize-css-assets-plugin 压缩 css

工程化

介绍一下 webpack 的构建流程

外围概念

  • entry:入口。webpack 是基于模块的,应用 webpack 首先须要指定模块解析入口(entry),webpack 从入口开始依据模块间依赖关系递归解析和解决所有资源文件。
  • output:输入。源代码通过 webpack 解决之后的最终产物。
  • loader:模块转换器。实质就是一个函数,在该函数中对接管到的内容进行转换,返回转换后的后果。因为 Webpack 只意识 JavaScript,所以 Loader 就成了翻译官,对其余类型的资源进行转译的预处理工作。
  • plugin:扩大插件。基于事件流框架 Tapable,插件能够扩大 Webpack 的性能,在 Webpack 运行的生命周期中会播送出许多事件,Plugin 能够监听这些事件,在适合的机会通过 Webpack 提供的 API 扭转输入后果。
  • module:模块。除了 js 领域内的 es module、commonJs、AMD 等,css @import、url(...)、图片、字体等在 webpack 中都被视为模块。

解释几个 webpack 中的术语

  • module:指在模块化编程中咱们把应用程序宰割成的独立性能的代码模块
  • chunk:指模块间依照援用关系组合成的代码块,一个 chunk 中能够蕴含多个 module
  • chunk group:指通过配置入口点(entry point)辨别的块组,一个 chunk group 中可蕴含一到多个 chunk
  • bundling:webpack 打包的过程
  • asset/bundle:打包产物

webpack 的打包思维能够简化为 3 点:

  • 所有源代码文件均可通过各种 Loader 转换为 JS 模块(module),模块之间能够相互援用。
  • webpack 通过入口点(entry point)递归解决各模块援用关系,最初输入为一个或多个产物包 js(bundle) 文件。
  • 每一个入口点都是一个块组(chunk group),在不思考分包的状况下,一个 chunk group 中只有一个 chunk,该 chunk 蕴含递归剖析后的所有模块。每一个 chunk 都有对应的一个打包后的输入文件(asset/bundle

打包流程

  1. 初始化参数:从配置文件和 Shell 语句中读取并合并参数,得出最终的配置参数。
  2. 开始编译:从上一步失去的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 办法开始执行编译。
  3. 确定入口:依据配置中的 entry 找出所有的入口文件。
  4. 编译模块:从入口文件登程,调用所有配置的 loader 对模块进行翻译,再找出该模块依赖的模块,这个步骤是递归执行的,直至所有入口依赖的模块文件都通过本步骤的解决。
  5. 实现模块编译:通过第 4 步应用 loader 翻译完所有模块后,失去了每个模块被翻译后的最终内容以及它们之间的依赖关系。
  6. 输入资源:依据入口和模块之间的依赖关系,组装成一个个蕴含多个模块的 chunk,再把每个 chunk 转换成一个独自的文件退出到输入列表,这一步是能够批改输入内容的最初机会。
  7. 输入实现:在确定好输入内容后,依据配置确定输入的门路和文件名,把文件内容写入到文件系统。

简版

  • Webpack CLI 启动打包流程;
  • 载入 Webpack 外围模块,创立 Compiler 对象;
  • 应用 Compiler 对象开始编译整个我的项目;
  • 从入口文件开始,解析模块依赖,造成依赖关系树;
  • 递归依赖树,将每个模块交给对应的 Loader 解决;
  • 合并 Loader 解决完的后果,将打包后果输入到 dist 目录。

在以上过程中,Webpack 会在特定的工夫点播送出特定的事件,插件在监听到相干事件后会执行特定的逻辑,并且插件能够调用 Webpack 提供的 API 扭转 Webpack 的运行后果

构建流程外围概念:

  • Tapable:一个基于公布订阅的事件流工具类,CompilerCompilation 对象都继承于 Tapable
  • Compiler:compiler 对象是一个全局单例,他负责把控整个 webpack 打包的构建流程。在编译初始化阶段被创立的全局单例,蕴含残缺配置信息、loaders、plugins 以及各种工具办法
  • Compilation:代表一次 webpack 构建和生成编译资源的的过程,在 watch 模式下每一次文件变更触发的从新编译都会生成新的 Compilation 对象,蕴含了以后编译的模块 module, 编译生成的资源,变动的文件, 依赖的状态等
  • 而每个模块间的依赖关系,则依赖于 AST 语法树。每个模块文件在通过 Loader 解析实现之后,会通过 acorn 库生成模块代码的 AST 语法树,通过语法树就能够剖析这个模块是否还有依赖的模块,进而持续循环执行下一个模块的编译解析。

最终 Webpack 打包进去的 bundle 文件是一个 IIFE 的执行函数。

// webpack 5 打包的 bundle 文件内容

(() => { // webpackBootstrap
    var __webpack_modules__ = ({'file-A-path': ((modules) => {// ...})
        'index-file-path': ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {// ...})
    })

    // The module cache
    var __webpack_module_cache__ = {};

    // The require function
    function __webpack_require__(moduleId) {
        // Check if module is in cache
        var cachedModule = __webpack_module_cache__[moduleId];
        if (cachedModule !== undefined) {return cachedModule.exports;}
        // Create a new module (and put it into the cache)
        var module = __webpack_module_cache__[moduleId] = {
                // no module.id needed
                // no module.loaded needed
                exports: {}};

        // Execute the module function
        __webpack_modules__[moduleId](module, module.exports, __webpack_require__);

        // Return the exports of the module
        return module.exports;
    }

    // startup
    // Load entry module and return exports
    // This entry module can't be inlined because the eval devtool is used.
    var __webpack_exports__ = __webpack_require__("./src/index.js");
})

webpack 具体工作流程

watch 的了解

watch没有缓存性,更多的是察看的作用,能够监听某些数据执行回调。当咱们须要 深度监听对象中 的属性时,能够关上 deep:true 选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话能够应用字符串模式监听

留神:Watcher : 观察者对象 , 实例分为 渲染 watcher (render watcher),计算属性 watcher (computed watcher),侦听器 watcher(user watcher)三种

diff 算法是怎么运作

每一种节点类型有本人的属性,也就是 prop,每次进行 diff 的时候,react 会先比拟该节点类型,如果节点类型不一样,那么 react 会间接删除该节点,而后间接创立新的节点插入到其中,如果节点类型一样,那么会比拟 prop 是否有更新,如果有 prop 不一样,那么 react 会断定该节点有更新,那么重渲染该节点,而后在对其子节点进行比拟,一层一层往下,直到没有子节点

  • 把树形构造依照层级合成,只比拟同级元素。
  • 给列表构造的每个单元增加惟一的 key 属性,不便比拟。
  • React 只会匹配雷同 classcomponent(这外面的 class 指的是组件的名字)
  • 合并操作,调用 componentsetState 办法的时候, React 将其标记为 – dirty. 到每一个事件循环完结, React 查看所有标记 dirtycomponent从新绘制.
  • 抉择性子树渲染。开发人员能够重写 shouldComponentUpdate 进步 diff 的性能

优化⬇️

为了升高算法复杂度,Reactdiff 会预设三个限度:

  1. 只对同级元素进行 Diff。如果一个DOM 节点 在前后两次更新中逾越了层级,那么 React 不会尝试复用他。
  2. 两个不同类型的元素会产生出不同的树。如果元素由 div 变为 p,React 会销毁div 及其子孙节点,并新建 p 及其子孙节点。
  3. 开发者能够通过 key prop来暗示哪些子元素在不同的渲染下能保持稳定。思考如下例子:

Diff 的思路

该如何设计算法呢?如果让我设计一个Diff 算法,我首先想到的计划是:

  1. 判断以后节点的更新属于哪种状况
  2. 如果是 新增,执行新增逻辑
  3. 如果是 删除,执行删除逻辑
  4. 如果是 更新,执行更新逻辑
  5. 按这个计划,其实有个隐含的前提——不同操作的优先级是雷同的
  6. 然而 React 团队 发现,在日常开发中,相较于 新增 删除 更新 组件产生的频率更高。所以 Diff 会优先判断以后节点是否属于 更新

基于以上起因,Diff 算法 的整体逻辑会经验两轮遍历:

  • 第一轮遍历:解决 更新 的节点。
  • 第二轮遍历:解决剩下的不属于 更新 的节点。

diff 算法的作用

计算出 Virtual DOM 中真正变动的局部,并只针对该局部进行原生 DOM 操作,而非从新渲染整个页面。

传统 diff 算法

通过循环递归对节点进行顺次比照,算法复杂度达到 O(n^3),n 是树的节点数,这个有多可怕呢?——如果要展现 1000 个节点,得执行上亿次比拟。。即使是 CPU 快能执行 30 亿条命令,也很难在一秒内计算出差别。

React 的 diff 算法

  1. 什么是和谐?

将 Virtual DOM 树转换成 actual DOM 树的起码操作的过程 称为 和谐。

  1. 什么是 React diff 算法?

diff算法是和谐的具体实现。

diff 策略

React 用 三大策略 将 O(n^3)复杂度 转化为 O(n)复杂度

策略一(tree diff):

  • Web UI 中 DOM 节点跨层级的挪动操作特地少,能够忽略不计。

策略二(component diff):

  • 领有雷同类的两个组件 生成类似的树形构造,
  • 领有不同类的两个组件 生成不同的树形构造。

策略三(element diff):

对于同一层级的一组子节点,通过惟一 id 辨别。

tree diff

  • React 通过 updateDepth 对 Virtual DOM 树进行层级管制。
  • 对树分层比拟,两棵树 只对同一档次节点 进行比拟。如果该节点不存在时,则该节点及其子节点会被齐全删除,不会再进一步比拟。
  • 只需遍历一次,就能实现整棵 DOM 树的比拟。

那么问题来了,如果 DOM 节点呈现了跨层级操作,diff 会咋办呢?

答:diff 只简略思考同层级的节点地位变换,如果是跨层级的话,只有创立节点和删除节点的操作。

如上图所示,以 A 为根节点的整棵树会被从新创立,而不是挪动,因而 官网倡议不要进行 DOM 节点跨层级操作,能够通过 CSS 暗藏、显示节点,而不是真正地移除、增加 DOM 节点

component diff

React 对不同的组件间的比拟,有三种策略

  1. 同一类型的两个组件,按原策略(层级比拟)持续比拟 Virtual DOM 树即可。
  2. 同一类型的两个组件,组件 A 变动为组件 B 时,可能 Virtual DOM 没有任何变动,如果晓得这点(变换的过程中,Virtual DOM 没有扭转),可节俭大量计算工夫,所以 用户 能够通过 shouldComponentUpdate() 来判断是否须要 判断计算。
  3. 不同类型的组件,将一个(将被扭转的)组件判断为dirty component(脏组件),从而替换 整个组件的所有节点。

留神:如果组件 D 和组件 G 的构造类似,然而 React 判断是 不同类型的组件,则不会比拟其构造,而是删除 组件 D 及其子节点,创立组件 G 及其子节点。

element diff

当节点处于同一层级时,diff 提供三种节点操作:删除、插入、挪动。

  • 插入:组件 C 不在汇合(A,B)中,须要插入
  • 删除:

    • 组件 D 在汇合(A,B,D)中,但 D 的节点曾经更改,不能复用和更新,所以须要删除 旧的 D,再创立新的。
    • 组件 D 之前在 汇合(A,B,D)中,但汇合变成新的汇合(A,B)了,D 就须要被删除。
  • 挪动:组件 D 曾经在汇合(A,B,C,D)里了,且汇合更新时,D 没有产生更新,只是地位扭转,如新汇合(A,D,B,C),D 在第二个,毋庸像传统 diff,让旧汇合的第二个 B 和新汇合的第二个 D 比拟,并且删除第二个地位的 B,再在第二个地位插入 D,而是(对同一层级的同组子节点)增加惟一 key 进行辨别,挪动即��。

总结

  1. tree diff:只比照同一层的 dom 节点,疏忽 dom 节点的跨层级挪动

如下图,react 只会对雷同色彩方框内的 DOM 节点进行比拟,即同一个父节点下的所有子节点。当发现节点不存在时,则该节点及其子节点会被齐全删除掉,不会用于进一步的比拟。

这样只须要对树进行一次遍历,便能实现整个 DOM 树的比拟。

这就意味着,如果 dom 节点产生了跨层级挪动,react 会删除旧的节点,生成新的节点,而不会复用。

  1. component diff:如果不是同一类型的组件,会删除旧的组件,创立新的组件
  1. element diff:对于同一层级的一组子节点,须要通过惟一 id 进行来辨别
  2. 如果没有 id 来进行辨别,一旦有插入动作,会导致插入地位之后的列表全副从新渲染
  3. 这也是为什么渲染列表时为什么要应用惟一的 key。

diff 的有余与待优化的中央

尽量减少相似将最初一个节点挪动到列表首部的操作,当节点数量过大或更新操作过于频繁时,会影响 React 的渲染性能

与其余框架相比,React 的 diff 算法有何不同?

diff 算法探讨的就是虚构 DOM 树发生变化后,生成 DOM 树更新补丁的形式。它通过比照新旧两株虚构 DOM 树的变更差别,将更新补丁作用于实在 DOM,以最小老本实现视图更新

具体的流程是这样的:

  • 实在 DOM 与虚构 DOM 之间存在一个映射关系。这个映射关系依附初始化时的 JSX 建设实现;
  • 当虚构 DOM 发生变化后,就会依据差距计算生成 patch,这个 patch 是一个结构化的数据,内容蕴含了减少、更新、移除等;
  • 最初再依据 patch 去更新实在的 DOM,反馈到用户的界面上。

在答复有何不同之前,首先须要阐明下什么是 diff 算法。

  • diff 算法是指生成更新补丁的形式 ,次要利用于 虚构 DOM 树变动后,更新实在 DOM。所以 diff 算法肯定存在这样一个过程:触发更新 → 生成补丁 → 利用补丁
  • React 的 diff 算法,触发更新的机会次要在 state 变动与 hooks 调用之后。此时触发虚构 DOM 树变更遍历,采纳了深度优先遍历算法。但传统的遍历形式,效率较低。为了优化效率,应用了分治的形式。将繁多节点比对转化为了 3 种类型节点的比对 ,别离是 树、组件及元素,以此晋升效率。

    • 树比对:因为网页视图中较少有跨层级节点挪动,两株虚构 DOM 树只对同一档次的节点进行比拟。
    • 组件比对:如果组件是同一类型,则进行树比对,如果不是,则间接放入到补丁中。
    • 元素比对:次要产生在同层级中,通过标记节点操作生成补丁,节点操作对应实在的 DOM 剪裁操作。同一层级的子节点,能够通过标记 key 的形式进行列表比照。
  • 以上是经典的 React diff 算法内容。自 React 16 起,引入了 Fiber 架构 。为了使整个更新过程 可随时暂停复原,节点与树别离采纳了 FiberNode 与 FiberTree 进行重构fiberNode 应用了双链表的构造,能够间接找到兄弟节点与子节点
  • 而后拿 Vue 和 Preact 与 React 的 diff 算法进行比照

    • PreactDiff 算法相较于 React,整体设计思路类似,但最底层的元素采纳了实在 DOM 比照操作,也没有采纳 Fiber 设计。Vue 的 Diff 算法整体也与 React 类似,同样未实现 Fiber 设计
  • 而后进行横向比拟,React 领有残缺的 Diff 算法策略,且领有随时中断更新的工夫切片能力,在大批量节点更新的极其状况下,领有更敌对的交互体验。
  • Preact 能够在一些对性能要求不高,仅须要渲染框架的简略场景下利用。
  • Vue 的整体 diff 策略与 React 对齐,尽管不足工夫切片能力,但这并不意味着 Vue 的性能更差,因为在 Vue 3 初期引入过,前期因为收益不高移除掉了。除了高帧率动画,在 Vue 中其余的场景简直都能够应用防抖和节流去进步响应性能。

学习原理的目标就是利用。那如何依据 React diff 算法原理优化代码呢?这个问题其实按优化形式逆向答复即可。

  • 依据 diff 算法的设计准则,应尽量避免跨层级节点挪动。
  • 通过设置惟一 key 进行优化,尽量减少组件层级深度。因为过深的层级会加深遍历深度,带来性能问题。
  • 设置 shouldComponentUpdate 或者 React.pureComponet 缩小 diff 次数。

vue 渲染过程

  • 调用 compile 函数, 生成 render 函数字符串 , 编译过程如下:

    • parse 应用大量的正则表达式对 template 字符串进行解析,将标签、指令、属性等转化为形象语法树 AST。模板 -> AST(最耗费性能)
    • optimize 遍历 AST,找到其中的一些动态节点并进行标记,不便在页面重渲染的时候进行 diff 比拟时,间接跳过这一些动态节点,优化 runtime 的性能
    • generate 将最终的 AST 转化为 render 函数字符串
  • 调用 new Watcher 函数, 监听数据的变动, 当数据发生变化时,Render 函数执行生成 vnode 对象
  • 调用 patch 办法, 比照新旧 vnode 对象, 通过 DOM diff 算法, 增加、批改、删除真正的 DOM 元素

React 有哪些优化性能的伎俩

类组件中的优化伎俩

  • 应用纯组件 PureComponent 作为基类。
  • 应用 shouldComponentUpdate 生命周期函数来自定义渲染逻辑。

办法组件中的优化伎俩

  • 应用 React.memo 高阶函数包装组件,React.memo 能够实现相似于 shouldComponentUpdate 或者 PureComponent 的成果
  • 应用 useMemo

    • 应用 React.useMemo 精细化的管控,useMemo 管制的则是是否须要反复执行某一段逻辑,而React.memo 管制是否须要重渲染一个组件
  • 应用 useCallBack

其余形式

  • 在列表须要频繁变动时,应用惟一 id 作为 key,而不是数组下标。
  • 必要时通过扭转 CSS 款式暗藏显示组件,而不是通过条件判断显示暗藏组件。
  • 应用 Suspense 和 lazy 进行懒加载,例如:
import React, {lazy, Suspense} from "react";

export default class CallingLazyComponents extends React.Component {render() {
    var ComponentToLazyLoad = null;

    if (this.props.name == "Mayank") {ComponentToLazyLoad = lazy(() => import("./mayankComponent"));
    } else if (this.props.name == "Anshul") {ComponentToLazyLoad = lazy(() => import("./anshulComponent"));
    }

    return (
      <div>
        <h1>This is the Base User: {this.state.name}</h1>
        <Suspense fallback={<div>Loading...</div>}>
          <ComponentToLazyLoad />
        </Suspense>
      </div>
    )
  }
}

如何判断一个对象是否属于某个类?

  • 第一种形式,应用 instanceof 运算符来判断构造函数的 prototype 属性是否呈现在对象的原型链中的任何地位。
  • 第二种形式,通过对象的 constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,然而这种形式不是很平安,因为 constructor 属性能够被改写。
  • 第三种形式,如果须要判断的是某个内置的援用类型的话,能够应用 Object.prototype.toString() 办法来打印对象的[[Class]] 属性来进行判断。

null 和 undefined 区别

首先 Undefined 和 Null 都是根本数据类型,这两个根本数据类型别离都只有一个值,就是 undefined 和 null。

undefined 代表的含意是 未定义 ,null 代表的含意是 空对象。个别变量申明了但还没有定义的时候会返回 undefined,null 次要用于赋值给一些可能会返回对象的变量,作为初始化。

undefined 在 JavaScript 中不是一个保留字,这意味着能够应用 undefined 来作为一个变量名,然而这样的做法是十分危险的,它会影响对 undefined 值的判断。咱们能够通过一些办法取得平安的 undefined 值,比如说 void 0。

当对这两种类型应用 typeof 进行判断时,Null 类型化会返回“object”,这是一个历史遗留的问题。当应用双等号对两种类型的值进行比拟时会返回 true,应用三个等号时会返回 false。

createElement 过程

React.createElement():依据指定的第一个参数创立一个 React 元素

React.createElement(
  type,
  [props],
  [...children]
)
  • 第一个参数是必填,传入的是似 HTML 标签名称,eg: ul, li
  • 第二个参数是选填,示意的是属性,eg: className
  • 第三个参数是选填, 子节点,eg: 要显示的文本内容
// 写法一:var child1 = React.createElement('li', null, 'one');
    var child2 = React.createElement('li', null, 'two');
    var content = React.createElement('ul', { className: 'teststyle'}, child1, child2); // 第三个参数能够离开也能够写成一个数组
      ReactDOM.render(
          content,
        document.getElementById('example')
      );

// 写法二:var child1 = React.createElement('li', null, 'one');
    var child2 = React.createElement('li', null, 'two');
    var content = React.createElement('ul', { className: 'teststyle'}, [child1, child2]);
      ReactDOM.render(
          content,
        document.getElementById('example')
      );

forEach 和 map 办法有什么区别

这办法都是用来遍历数组的,两者区别如下:

  • forEach()办法会针对每一个元素执行提供的函数,对数据的操作会扭转原数组,该办法没有返回值;
  • map()办法不会扭转原数组的值,返回一个新数组,新数组中的值为原数组调用函数解决之后的值;

常见的 HTTP 申请头和响应头

HTTP Request Header 常见的申请头:

  • Accept: 浏览器可能解决的内容类型
  • Accept-Charset: 浏览器可能显示的字符集
  • Accept-Encoding:浏览器可能解决的压缩编码
  • Accept-Language:浏览器以后设置的语言
  • Connection:浏览器与服务器之间连贯的类型
  • Cookie:以后页面设置的任何 Cookie
  • Host:发出请求的页面所在的域
  • Referer:发出请求的页面的 URL
  • User-Agent:浏览器的用户代理字符串

HTTP Responses Header 常见的响应头:

  • Date:示意音讯发送的工夫,工夫的形容格局由 rfc822 定义
  • server: 服务器名称
  • Connection:浏览器与服务器之间连贯的类型
  • Cache-Control:管制 HTTP 缓存
  • content-type: 示意前面的文档属于什么 MIME 类型

常见的 Content-Type 属性值有以下四种:

(1)application/x-www-form-urlencoded:浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 形式提交数据。该种形式提交的数据放在 body 外面,数据依照 key1=val1&key2=val2 的形式进行编码,key 和 val 都进行了 URL 转码。

(2)multipart/form-data:该种形式也是一个常见的 POST 提交形式,通常表单上传文件时应用该种形式。

(3)application/json:服务器音讯主体是序列化后的 JSON 字符串。

(4)text/xml:该种形式次要用来提交 XML 格局的数据。

position 的属性有哪些,区别是什么

position 有以下属性值:

属性值 概述
absolute 生成相对定位的元素,绝对于 static 定位以外的一个父元素进行定位。元素的地位通过 left、top、right、bottom 属性进行规定。
relative 生成绝对定位的元素,绝对于其原来的地位进行定位。元素的地位通过 left、top、right、bottom 属性进行规定。
fixed 生成相对定位的元素,指定元素绝对于屏幕视⼝(viewport)的地位来指定元素地位。元素的地位在屏幕滚动时不会扭转,⽐如回到顶部的按钮⼀般都是⽤此定位⽅式。
static 默认值,没有定位,元素呈现在失常的文档流中,会疏忽 top, bottom, left, right 或者 z-index 申明,块级元素从上往下纵向排布,⾏级元素从左向右排列。
inherit 规定从父元素继承 position 属性的值

后面三者的定位形式如下:

  • relative: 元素的定位永远是绝对于元素本身地位的,和其余元素没关系,也不会影响其余元素。
  • fixed: 元素的定位是绝对于 window(或者 iframe)边界的,和其余元素没有关系。然而它具备破坏性,会导致其余元素地位的变动。
  • absolute: 元素的定位绝对于前两者要简单许多。如果为 absolute 设置了 top、left,浏览器会依据什么去确定它的纵向和横向的偏移量呢?答案是浏览器会递归查找该元素的所有父元素,如果找到一个设置了 position:relative/absolute/fixed 的元素,就以该元素为基准定位,如果没找到,就以浏览器边界定位。如下两个图所示:

介绍一下 Vue 中的 Diff 算法

在新老虚构 DOM 比照时

  • 首先,比照节点自身,判断是否为同一节点,如果不为雷同节点,则删除该节点从新创立节点进行替换
  • 如果为雷同节点,进行 patchVnode,判断如何对该节点的子节点进行解决,先判断一方有子节点一方没有子节点的状况(如果新的 children 没有子节点,将旧的子节点移除)
  • 比拟如果都有子节点,则进行 updateChildren,判断如何对这些新老节点的子节点进行操作(diff 外围)。匹配时,找到雷同的子节点,递归比拟子节点

在 diff 中,只对同层的子节点进行比拟,放弃跨级的节点比拟,使得工夫简单从 O(n^3)升高值 O(n),也就是说,只有当新旧 children 都为多个子节点时才须要用外围的 Diff 算法进行同层级比拟。

HTTP 协定的长处和毛病

HTTP 是超文本传输协定,它定义了客户端和服务器之间替换报文的格局和形式,默认应用 80 端口。它应用 TCP 作为传输层协定,保障了数据传输的可靠性。

HTTP 协定具备以下 长处

  • 反对客户端 / 服务器模式
  • 简略疾速:客户向服务器申请服务时,只需传送申请办法和门路。因为 HTTP 协定简略,使得 HTTP 服务器的程序规模小,因此通信速度很快。
  • 无连贯:无连贯就是限度每次连贯只解决一个申请。服务器解决完客户的申请,并收到客户的应答后,即断开连接,采纳这种形式能够节俭传输工夫。
  • 无状态:HTTP 协定是无状态协定,这里的状态是指通信过程的上下文信息。短少状态意味着如果后续解决须要后面的信息,则它必须重传,这样可能会导致每次连贯传送的数据量增大。另一方面,在服务器不须要先前信息时它的应答就比拟快。
  • 灵便:HTTP 容许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记。

HTTP 协定具备以下 毛病

  • 无状态: HTTP 是一个无状态的协定,HTTP 服务器不会保留对于客户的任何信息。
  • 明文传输: 协定中的报文应用的是文本模式,这就间接裸露给外界,不平安。
  • 不平安

(1)通信应用明文(不加密),内容可能会被窃听;
(2)不验证通信方的身份,因而有可能遭逢假装;
(3)无奈证实报文的完整性,所以有可能已遭篡改;

退出移动版