javascript面试题(初/中级前端开发工程师)

promise的基础使用

Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;

从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。

ES6 语言标准,统一了用法,原生提供了Promise对象。

*ES6 规定,Promise对象是一个构造函数,用来生成Promise实例

promise有三种状态:

pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会 再变。创造promise实例后,它会立即执行

promise是用来解决两个问题的:

回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象

promise可以支持多个并发的请求,获取并发请求中的数据

这个promise可以解决异步的问题,本身不能说promise是异步的

promise的使用场景

场景1:获取文件信息。

场景2:配合AJAX获取信息

场景3:解决回调地狱,实现串行任务队列。

场景4: node中进行本地操作的异步过程

promise.all()参数

promise.all()用于一个异步操作需要在几个异步操作完成后再进行时使用。

promise.all()接受一个promise对象组成的数组参数,返回promise对象。

当数组中所有promise都完成了,就执行当前promise对象的then方法,如果数组中有一个promise执行失败

了,就执行当前promise对象的catch方法。

Promise的两个特点(优点)

1、Promise对象的状态不受外界影响

Promise 有三种状态,只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这个状态

2、Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆,只能由 pending变成fulfilled或者由pending变成rejected

Promise的三个缺点

1)无法取消Promise,一旦新建它就会立即执行,无法中途取消

2)如果不设置回调函数,Promise内部抛出的错误,不会反映到外部

3)当处于pending状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成

方法 then() 然后 : - 成功了做些什么 , 失败了做些什么;

如果then添加了第二个参数,这时候就不会执行catch里面的代码

catch() 失败之后会执行的回调函数;

finally() 无论成功失败都会调用的状态;

all () all里面的参数所有都成功才会成功 数组

race( 只要一个成功就成功

async/await的使用

es6解决异步代码

async函数的用法

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

返回一个promise对象

async函数返回一个 Promise 对象。

async函数内部return语句返回的值,会成为then方法回调函数的参数。

抛出错误代码

如果可能出错的代码比较少的时候可以使用try/catch结构来了处理,如果可能出错的代码比较多的时候,可以利

用async函数返回一个promise对象的原理来处理,给async修饰的函数调用后返回的promise对象,调用catch方

法来处理异常。

promise和async/await的区别

async await概念:解决方案,他遵循的是Generator 函数的语法糖,它返回的是一个Promise对象。

两者的区别:

Promise的出现解决了传统callback(靠百克)函数导致的“地域回调”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。而async await代码看起来会简洁些,使得异步代码看起来像同步代码,await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句。

async await与Promise一样,是非阻塞的。*

async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数

解决跨域的问题(3种)

jsonp

基本原理:利用 script (死亏瑞普特)标签的异步加载特性实现

给服务端传一个回调函数,服务器返回一个传递过去的回调函数名称的JS代码

这种方式是利用浏览器不限制某些标签发送跨域请求,例如link、img、iframe、script。通常请求请

求回来的资源要在js中进行处理,所以jsonp跨域是利用script标签进行发送,且这种请求方式只能

是get请求。

cors

这种方式是让接口资源方面进行授权,授权允许访问。在接口资源处添加响应头即可通过浏览器的

同源策略,响应头具体的键值对如下:

proxy

这种方式属于找外援的一种方式,浏览器只能限制当前正在打开的web页面发送请求,但无法限制

服务器端请求接口资源。所以我们可以将请求发送到自己服务器,然后自己服务器去请求目标接口

资源,最后自己服务器将接口资源返回给当前页面,类似于找外援代替自己请求目标接口资源。

这种方式通常要对服务器进行代理配置,需要对apache服务器、nginx服务器、nodejs服务器进行配

置。

null和undefined的区别

null是一个表示"无"的对象(空对象指针),转为数值时为0;

undefined是一个表示"无"的原始值,转为数值时为NaN。

拓展:

null表示"没有对象",即该处不应该有值。典型用法是:

作为函数的参数,表示该函数的参数不是对象。

作为对象原型链的终点。

undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。典型用法是:

变量被声明了,但没有赋值时,就等于undefined。

调用函数时,应该提供的参数没有提供,该参数等于undefined。

对象没有赋值的属性,该属性的值为undefined。

函数没有返回值时,默认返回undefined。

函数的节流和防抖

节流

节流是指当一个事件触发的时候,为防止事件的连续频繁触发,设置定时器,达到一种一段事件内只触发一次的效果,在

当前事件内不会再次触发,当前事件结束以后,再次触发才有效.

防抖

防抖是指当一个事件触发的时候, 为防止频繁触发事件, 设置定时器,以达到一种 频繁触发期间不处理, 只有当最后

一次连续触发结束以后才处理

箭头函数和普通函数的区别

1.箭头函数没有prototype(原型),箭头函数没有自己的this,继承的是外层代码块的this。

2.不可以当做构造函数,也就是说不可以使用new命令,否则会报错的。

3.不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

4.不可以使用yield命令,因此箭头函数不能用作 Generator(生成器) 函数。

5.因为没有this,所以不能使用call、bind、apply来改变this的指向。

箭头函数为什么不能new

因为箭头函数没有prototype也没有自己的this指向并且不可以使用arguments。

new一个对象的过程

1.开辟一个堆内存,创建一个空对象

2.执行构造函数,对这个空对象进行构造

3.给这个空对象添加proto属性

改变箭头函数(call,apply,bind)

  1. bind:

    bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)

    改变this指向后不会立即执行,而是返回一个永久改变this指向的函数

  2. call:

    call方法的第一个参数也是this的指向,后面传入的是一个参数列表

    apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

  3. apply :

    apply接受两个参数,第一个参数是this`的指向,第二个参数是函数接受的参数,以数组的形式传入

    改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

原型原型链

原型链:

JS每一次获取对象中的属性,都是一次查询的过程,首先在自身上进行查找,如果在自身属性中找不到,就会去原型对象中去查找,如果原型对象还是查不到就去原型对象的原型中去查找,直到查找到原型链的顶端,也就是Object原型。(Object的原型为null)

原型链的本质

子类构造函数的prototype__proto__指向父类构造器的prototype,建立对象之间的关联

原型:

原型就是一个为对象实例定义了一些公共属性和公共方法的对象模板

原型实例:

1.所有引用类型都有一个proto_(隐式原型)属性,属性值是一个普通的对象

2.所有函数都有一个prototype(原型)属性,属性值是一个普通的对象

3.所有引用类型的--proto--属性指向它构造函数的prototype

深拷贝浅拷贝

javascript中的两大数据类型:

基础数据类型: 基本类型的数据存放在栈内存中

复杂数据类型: 引用数据类型保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中

浅拷贝

只能拷贝一层,如果对象内还有复杂数据类型,那么没效果

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝

如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址

即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

浅拷贝的现象:

  • Object.assign

  • Array.prototype.slice(), Array.prototype.concat()

  • 使用拓展运算符实现的复制

扩展运算符(...)

将数组或者对象转换成参数序列,使用逗号分隔的序列

作用:

1、将数组、对象合并

2、函数剩余参数

深拷贝

不管有多少层数据,都能一次性拷贝过来,不管多少层的数据,互相之间操作都不会影响;深拷贝后的对象与原来的对象是完全隔离的,互不影响,map

拷堆里面的地址

深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

深拷贝的现象

  • _.cloneDeep()

  • jQuery.extend()

  • JSON.stringify()

深浅拷贝的区别(前提是拷贝的类型为引用数据类型)

浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址

但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象

闭包

闭包就是能够读取外层函数内部变量的函数

闭包的作用

  1. 在外部访问函数内部的变量

  2. 让函数内的局部变量可以一直保存下去

  3. 模块化私有属性和公共属性

闭包原理

全局变量生存周期是永久,局部变量生存周期随着函数的调用介绍而销毁。

闭包就是 在函数中定义且成为该函数内部返回的函数的自由变量 的变量,该变量不会随着外部函数调用结束而销毁。

(注:不光是变量,函数内声明的函数也可以形成闭包)

当函数可以记住并访问所在的词法作用域,即使函数是在当前词法作用域之外执行,这时就产生了闭包。

闭包的缺点

占用内层空间 大量使用闭包会造成 栈溢出,轻则影响系统性能,重则导致程序崩溃

简述闭包的问题以及优化

由于闭包会一直占用内存空间,直到页面销毁,我们可以主动将已使用的闭包销毁:

将闭包函数赋值为null 可以销毁闭包

闭包的优点

可以重复使用变量,并且不会造成变量污染

闭包为什么会造成内存泄漏

因为它把外部的包含它的函数的活动对象也包含在自己的作用域链中了,会比一般的函数占用更多内存。

除了闭包会造成内存泄漏还有什么会造成内存泄漏

意外的全局变量也会造成内存泄漏

被遗忘(没有清除)的定时器,回调函数(存在于内存中)也会造成内存泄漏

什么是内存泄露

是指一块被分配的内存即不能使用也不能回收内存泄漏严重就导致

es6

es6新增

  1. 模板字符串

  2. 数据类型Symbol

  3. let,const 声明变量

  4. 解构赋值(对象和数组的解构赋值)

  5. for of:循环,要注意的是for of不可以循环对象

  6. 展开运算符(…)可以将数组或对象展开为多个元素

  7. ES6箭头函数:当箭头函数的参数只有一个时可以省略小括号,当函数体只有一条语句的话,不写大括号会默认return

  8. class 类的继承ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念

  9. 使用新的super和extends关键字来扩展类(super必须在this之前被调用)

  10. 新增了数组方法 map()、filter()、reduce()、every()、some()等等

  11. 新增改变this指向的方法 call()、 apply()

let const var的区别

var 支持变量声明与解析 不支持块级作用域 允许重复声明

let 不支持变量的声明与解析 支持块级作用域 不允许重复声明

const 不支持变量的声明与解析 支持块级作用域 不允许重复声明 声明常量,一旦确定不能修改 声明后必须赋值 引用类型可以修改

map和foreach的区别

forEach()方法不会返回执行结果,而是undefined(安迪凡特)。也就是说,forEach()会修改原来的数组.

map()方法会得到一个新的数组并返回,

foreach和for循环的区别

1.for循环可以使用break跳出循环,但forEach不能。

2.for循环可以控制循环起点(i初始化的数字决定循环的起点),forEach只能默认从索引0开始。

3.for循环过程中支持修改索引(修改 i),但forEach做不到(底层控制index自增,无法左右它)

foreach, for in , for of, for的使用场景

foreach: 遍历数组,只有循环数组的时候使用。

for of: 遍历对象和数组。遍历对象需要通过和Object.keys()

for in : 遍历对象因为对象内key没有规律不能用for来遍历所以只能用for in。

for: 是你需要循环的时候就可以用,比如你需要重复执行代码那么就可以循环。

数组的合并方法

es6新增 ... 扩张运算符

let arr3 = [1,2].concat([3,[4]])

如何实现继承

对于 JavaScript 来说,继承有两个要点:

  1. 复用父构造函数中的代码

  2. 复用父原型中的代码第一种实现复用父构造函数中的代码,我们可以考虑调用父构造函数并将 this 绑定到子构造函数。

第一种方法:复用父原型中的代码,我们只需改变原型链即可。将子构造函数的原型对象的 proto属性指向父构造函数的原型对象。

第二种实现: 使用 new 操作符来替代直接使用 proto 属性来改变原型链。

第三种实现: 使用一个空构造函数来作为中介函数,这样就不会将构造函数中的属性混到 prototype 中

继承

原型继承原型里放着父元素的实例对象

借用构造函数通过call方法改变父元素的this指向

组合继承: 原型继承和借用构造函数一起使用

继承的方法

一丶原型链继承

基本原理是:将父类的实例赋值给子类的原型。

这种继承方式的缺点:子类的实例可以访问父类的私有属性,子类的实例还可以更改该属性,这样不安全。

二丶构造函数继承

原理:在子类构造函数中,使用call来将子类的this绑定到父类中去

优点:

借用构造函数法可以解决原型中引用类型值被修改的问题;

缺点:

只能继承父对象的实例属性和方法,不能继承父对象原型属性和方法

三丶组合继承

将原型继承和借用构造函数两种方式组合起来

优点:

可以保证每个函数有自己的属性,可以解决原型中引用类型值被修改的问题;

子类的实例可以继承父类原型上面的属性和方法

缺点:

在实例化子类的过程中,父类构造函数调用了两次

四丶寄生组合式继承

所谓寄生继承:通过 Object.create(库瑞提) 将子类的原型继承到父类的原型上。

特点:

这种方式的高效率体现在纸雕用了一次父类构造函数,并且因此避免了在父类的prototype(普提太破) 上面创建不必要的、多余的属性。

同时,原型链还能保持不变,可以正常使用 instanceof 和 isPrototypeOf 。

开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

五丶class extend 继承

ES6 中有了类的概念,可以通过 class (克拉斯)声明一个类,通过 extends (艾克斯单死)关键字来实现继承关系。

class 与 ES5 构造函数的主要区别:

class 只能通过 new 来调用,而构造函数则可以直接调用;

class 内部所有定义的方法,都是不可枚举的(non-enumerable)

值得注意的是:

Super关键字表示父类的构造函数,相当于 ES5 的 Parent.call(this)。

子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工,如果不调用 super方法,子类就得不到 this 对象。

正是因为这个原因,在子类的构造函数中,只有调用 super 之后,才可以使用 this 关键字,否则会报错。

子类的 proto 属性指向父类因此,子类可以继承父类的静态方法。子类的原型的 proto,总是指向父类的 prototype 属性

es6继承 (class)

ES6 入门教程

Class 可以通过extends关键字实现继承,让子类继承父类的属性和方法

es6继承的机制

ES6 的继承机制,则是先将父类的属性和方法,加到一个空的对象上面,然后再将该对象作为子类的实例,即“继承在前,实例在后”。

这就是为什么 ES6 的继承必须先调用super()方法,因为这一步会生成一个继承父类的this对象,没有这一步就无法继承父类。这意味着新建子类实例时,父类的构造函数必定会先运行

基本数据类型Symbol

Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数

返回的symbol是唯一的

作为属性名的Symbol

对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性

Symbol 值作为对象属性名的时候:

Symbol 值作为对象属性名时,不能用点运算符。因为点运算符后面总是字符串 必须使用方括号访问这些属性属性的名字是symbol`而不是一个字符串。除此之外,它与一个普通的属性没有什么区别

!!!!!!!!!!!!!! 不能使用new创建Symbol

事件轮询(EventLoop)

一个用来等待和发送消息和事件的程序结构

1, 所有任务都在主线程上执行,形成一个执行栈。

2、主线程发现有异步任务,如果是微任务就把他放到微任务的消息队列里,如果是宏任务就把他

放到宏任务的消息队列里。

3、执行栈所有同步任务执行完毕。

4、执行微任务队列,之后再执行宏任务队列。

5、轮询第4步。

事件轮询的优先级

一个事件循环是可以有多个任务队列的,每个任务都有一个任务源,相同任务源的任务只能在一个任务队列中,一个任务队列的执行顺序是先进先出,多个任务队列有优先级,但根据宿主环境的不同,优先级也不同,不能保证固定。

微任务宏任务的读取顺序

根据任务分类,又将任务队列分为宏观任务队列(microtask queue)和微观任务队列(microtask queue)。任务队列的读取顺序是先读取所有微观任务队列执行后再读取一个宏观任务队列,再读取所有微观任务队列,再读取一个宏观任务队列

事件轮询的执行

微任务队列优先于宏任务队列执行,微任务队列上创建的宏任务会被后添加到当前宏任务队列的尾端,微任务队列中创建的微任务会被添加到微任务队列的尾端。只要微任务队列中还有任务,宏任务队列就只会等待微任务队列执行完毕后再执行

宏任务的特性

宏任务所处的队列就是宏任务队列

第一个宏任务队列中只有一个任务:执行主线程上的JS代码;如果遇到上方表格中的异步任务,会创建出一个新的宏任务队列,存放这些异步函数执行完成后的回调函数。

宏任务队列可以有多个

宏任务中可以创建微任务,且如果该微任务立即被加入执行栈的话,会打断当前宏任务的执行。(EXP3)

当一个宏任务队列中的任务全部执行完后,会查看是否有微任务队列,如果有就会优先执行微任务队列中的所有任务,如果没有就查看是否有宏任务队列

微任务的特性

微任务所处的队列就是微任务队列

在上一个宏任务队列执行完毕后,如果有微任务队列就会执行微任务队列中的所有任务

new promise((resolve)=>{ 这里的函数在当前队列直接执行 }).then( 这里的函数放在微任务队列中执行 )

微任务队列上创建的微任务,仍会阻碍后方将要执行的宏任务队列 (EXP2)

由微任务创建的宏任务,会被丢在异步宏任务队列中执行 (EXP4)

http

http的理解:

HTTP 协议是超文本传输协议,是客户端浏览器或其他程序“请求”与 Web 服务器响应之间的应用层通信协议。

HTTPS主要是由HTTP+SSL构建的可进行加密传输、身份认证的一种安全通信通道。

http和https的区别

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

http常见的响应状态

100——客户必须继续发出请求

101——客户要求服务器根据请求转换HTTP协议版本

200——交易成功

201——提示知道新文件的URL

202——接受和处理、但处理未完成

203——返回信息不确定或不完整

204——请求收到,但返回信息为空

205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件

206——服务器已经完成了部分用户的GET请求

300——请求的资源可在多处得到

301——删除请求数据

302——在其他地址发现了请求数据

303——建议客户访问其他URL或访问方式

304——客户端已经执行了GET,但文件未变化

305——请求的资源必须从服务器指定的地址得到

306——前一版本HTTP中使用的代码,现行版本中不再使用

307——申明请求的资源临时性删除

400——错误请求,如语法错误

401——请求授权失败

402——保留有效ChargeTo头响应

403——请求不允许

404——没有发现文件、查询或URl

405——用户在Request-Line字段定义的方法不允许

406——根据用户发送的Accept拖,请求资源不可访问

407——类似401,用户必须首先在代理服务器上得到授权

408——客户端没有在用户指定的饿时间内完成请求

409——对当前资源状态,请求不能完成

410——服务器上不再有此资源且无进一步的参考地址

411——服务器拒绝用户定义的Content-Length属性请求

412——一个或多个请求头字段在当前请求中错误

413——请求的资源大于服务器允许的大小

414——请求的资源URL长于服务器允许的长度

415——请求资源不支持请求项目格式

416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If-Range请求头 字段

417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求

500——服务器产生内部错误

501——服务器不支持请求的函数

502——服务器暂时不可用,有时是为了防止发生系统过载

503——服务器过载或暂停维修

504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长

505——服务器不支持或拒绝支请求头中指定的HTTP版本

http请求有哪些他们的区别是什么

常见的有5种,分别是get、head,post、put、 delete

最常见的HTTP请求方法是GET 和 POST。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。

GET:它是最常见的方法,用于获取资源,常用于向服务器查询某些信息。打开网页一般都是用

GET方法,因为要从 Web 服务器获取信息

HEAD:类似于 GET请求,只不过返回的响应中没有具体的内容,用于获取报头。

POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件), 数据被包含在请求体

中。POST请求可能会导致新的资源的建立和/或对已有资源的修改。

PUT:从客户端向服务器传送的数据取代指定文档的内容。

DELETE:请求服务器删除指定的页面。

get和post的区别

GET提交的数据会放在?之后,以问号(?)分割URL 和传输数据,参数之间以&相连

GET提交的数据大小有限制(因为浏览器对URL的长度有限制), 而POST 方法提交的数据大小没有限制。

GET方式提交数据会带来安全问题,比如一个登录页面通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码。

get 在请求头内不安全

post在请求体内相对安全

url

url的组成

http:/https: 协议

www.baidu.com 域名

:8080 端口

/sf/vsearch 路径

?wd=百度热搜 查询(可有可无)

#a=1&b=2 哈希值(可有可无)

url的执行顺序

输入URL

访问hosts解析,如果没有解析访问DNS解析

TCP握手

HTTP请求

HTTP响应返回数据

浏览器解析并渲染页面

同源策略

域名 , 协议, 端口相同,也就是在同一个域里

非同源受到的限制

  • cookie不能读取 (如我在自己的站点无法读取博客园用户的cookie)

  • dom无法获得

  • ajax请求不能发送

localstorage, sessionstorage,cookie的区别

本地储存的区别

1.储存大小 cookie 4KB 左右 localStorage/sessionStorage 20MB 左右 2.自动携带 cookie 会跟随请求自动携带在请求体内 localStorage/sessionStorage 不会自动携带需要手动设置 3.时效性 localStorage 永久 sessionStorage 会话 cookie 默认会话级别,可以自定义设置过期时间 4.储存位置 cookie 必须依赖域名储存 localStorage/sessionStorage 可以在本地状态下使用 5.前后端操作 cookie 前后端都可以操作 localStorage/sessionStorage 只能前端操作,后端不行 +session(补充) 一个服务器端的储存空间用来支持登录状态保持的当 session 空间开启以后,会生成一个 sessionID 的内容自动放在 cookie 内操作cookie

cookie的使用场景

+是一个在客服端的储存空间 +可以向里面储存一些数据

cookie 的特点(记住)

1.储存必须是字符串格式 必须是 'key=value; key2=value2; key3=value3' 2.储存的大小比较 4KB 左右 50条 左右 3.cookie 会跟随请求自动携带在请求头内 只要你的 cookie 空间内有数据 在当前域名下发送的所有请求,都会自动携带 cookie 空间内储存的数据 4.cookie 是按照域名储存的 哪一个域名下储存的 cookie 哪一个域名下使用 5.cookie 必须依赖域名储存 本地打开的文件是没办法储存和读取 cookie 的 6.cookie 可以设置时效性 默认时效性:会话级别的时效(关闭浏览器就没有了) 可以设置时效:7天,3小时.2分钟,.... 7.cookie 前后端都可以操作 前端通过 JS 语言可以操作 后端任何一个 计算机语言 都可以操作

设置 cookie

+语法:document.cookie = 'key(键名)=value(键值)', expires=时间对象;path=路径 +注意:一次只能设置一条,过期事件默认是 会话级别 +设置一条带有过期时间的 cookie 语法:document.cookie = 'key=value;修饰这个cokie的内容' 语法:document.cookie = 'key=value;expries=时间对象'

设置过期时间

+特点:不管比给他的是什么时间节点,他都会当做世界标准时间使用获取 cookie

获取一个完整字符串

+语法:document.cookie

localStorage的设置

localstorage中存储的必须是字符串

获取: window.localstorage.setitem()

保存: window.localstorage.getitem()

删除:window.localstorage.clear()

事件流

事件执行机制

普通函数同步操作调用栈直接执行

promise.asyc会到微任务队列,调用栈清空微任务队列,异步操作,axios,定时器会到消息队列,调用栈和微任务队列清空后执行消息队列

等待主线程中任务全部完成后,再回来把异步队列中任务放到主程序中运行,这样反复的循环,就是事件循环。

主线程运行的时候,会产生堆和栈。栈中的代码会调用各种外部 API。他们在任务队列中加入各种事件。只要栈中的代码执行完毕,主线程就会去读取“任务列队”,依次执行哪些事件所对应的回调函数。主线程从“任务队列”中读取事件,这个过程是循环不断的。所以叫事件循环机制。

事件的处理过程主要有三个阶段:

捕获阶段,目标阶段,冒泡阶段;

事件的处理过程主要有三个阶段:

捕获阶段,目标阶段,冒泡阶段;

捕获阶段

:当我们在 DOM 树的某个节点发生了一些操作(例如单击、鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到触发的目标节点。在到达目标节点之前的过程,就是捕获阶段。事件由页面元素接收,逐级向下,到具体的元素。

目标阶段:

当事件不断的传递直到目标节点的时候,最终在目标节点上触发这个事件,就是目标阶段。具体的元素本身。

冒泡阶段:

事件冒泡即事件开始时,由最具体的元素接收(也就是事件发生所在的节点),然后逐级传播到较为不具体的节点。跟捕获相反,具体元素本身,逐级向上,到页面元素(我们平时用的事件绑定就是利用的事件冒泡的原理)。

事件捕获:

当使用事件捕获时,父级元素先触发,子元素后触发。

事件冒泡:

当使用事件冒泡时,子级元素先触发,父元素后触发。

token(令牌)

token前端怎么校验

不能校验,后端给的

什么是token

token (计算机术语)在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般作为邀请、登录系统使用

token的意义

令牌:代表执行某些操作的权利和对象,访问令牌(Assess token)表示访问操作控制主题的系统对象,令牌是一种控制站点的特殊帧,以区别数据帧和其他控制帧

为什么要用token

token 完全由应用管理,所以它可以避开同源策略

注:什么是同源策略(所谓的同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。同源策略是必须的。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问)

2、Token 可以避免 CSRF攻击

注:(你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…造成的问题包括:个人隐私泄露以及财产安全。

3、Token 可以是无状态的,可以在多个服务间共享

token的 作用

用户身份的验证

Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位,token其实通俗点讲也可以理解为暗号,在数据传输之前要进行暗号对接和核对,不同的暗号被授权不听的数据操作,

token的使用流程

客户端使用用户名跟密码请求登录

服务端收到请求,去验证用户名与密码

验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端

客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里

客户端每次向服务端请求资源的时候需要带着服务端签发的 Token

服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

为什么token不存放在cookie里

token一般放在本地存储中。token的存在本身只关心请求的安全性,而不关心token本身的安全,因为token是服务 器端生成的,可以理解为一种加密技术。但如果存在cookie内的话,浏览器的请求默认会自动在请求头中携带 cookie,所以容易受到csrf攻击。

数据类型

基本数据类型:

undefined、null、number、boolean、string , symbol(es6中)

复杂数据类型:

Object、array、function(函数),正则

检测数据类型

  1. typeof 用来检测数据类型的运算符: 检测的不管是数组还是正则都返回的是"object",所以typeof不能判断一个值是否为数组 ,基本数据类型为string

  2. instanceof: instanceof可以检测数组和正则,基本数据类型的值是不能用instanceof来检测的 ,在类的原型继承中,instanceof检测出来的结果其实是不准确的,用instanceof检测的时候,只要当前的这个类在实例的原型链上(可以通过原型链proto找到它),检测出来的结果都是true。

  3. Object.prototype.toString.call(value) ->找到Object原型上的toString方法,让方法执行,并且让方法中的this变为value(value->就是我们要检测数据类型的值)。检测的类型比较多,也比较精准

判断对象为空

第一种: 使用JSON.stringify()将对象转换为json字符串

第二种: 使用for...in循环遍历对象除Symbol以外的所有可枚举属性,当对象有属性存在返回false, 否则返回 true。

第三种: Object.getOwnPropertyNames() 方法会返回该对象所有可枚举和不可枚举属性的属性名(不含Symbol 属性)组成的数组。然后再通过判断返回的数组长度是否为零,如果为零的话就是空对象

第四种: Object.keys() 是 ES5 新增的一个对象方法,该方法返回一个数组,包含指定对象自有的可枚举属性(不 含继承的和Symbol属性)。用此方法只需要判断返回的数组长度是否为零,如果为零的话就是空对象

axios

使用说明 · Axios 中文说明 · 看云

axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和node.js中axios是vue作者推荐使用的网络请求库

它具有以下特性:

  • 支持浏览器和node.js

  • 支持promise

  • 能够拦截 请求和响应

  • 自动转换json数据

axios除了支持传统的 GET 和 POST 方式以外,常见的请求方式还支持

  • put:修改数据

  • delete:删除数据

axios响应(ret)的主要属性:

  • data:实际响应回来的数据(最常用)

  • headers:响应头信息

  • status:响应状态码

  • statusText:响应状态信息

axios全局配置

  • axios.defaults.timeout = 3000【设置超时时间】

  • axios.defaults.baseURL = 'http://localhost/app'【设置默认地址】

  • axios.defaults.headers['token'] = '123123123'【设置请求头信息,通用头信息】

  • axios.defaults.headers.common['_token'] = '123123'【通用头信息,common可以不写】

注意:

axios发送post请求的时候,默认发送json格式数据如果需要发送post表单类型请求,则需要指定请求头

拦截器的目的(请求拦截器/响应拦截器)

在请求 发出去之前 / 收到响应之后 做一些操作

请求拦截器: 就是在所有请求发送之前会触发的一个函数,他就是一个中间件(官方说话),我们可以把需要大量携带的数据和大量请求都需要携带的数据,写在请求拦截器里面比如token

语法: axios.interceptors.request.use

响应拦截器: 每一个响应回到前端之后都会执行的一个函数,在响应拦截器里,统一把错误提出来,统一做错误处理

语法:axios.interceptors.response.use

axios的封装:

import axios from 'axios' 
​
import router from './../router' 
​
// development 开发环境 production 生产环境 
​
const isDev = process.env.NODE_ENV === 'development' 
​
// baseURL 会自动加到所有的请求之前 
​
const request = axios.create({ 
​
// http://121.89.205.189/api/pro/list ==> /pro/list 
​
baseURL: isDev ? 'http://121.89.205.189/api' : 'http://121.89.205.189/api', 
​
timeout: 6000 
​
})
​
// 请求拦截器 
​
request.interceptors.request.use(config => { 
​
// 传入token验证登录状态 
​
// 如果需要设置其他的信息也可以在这里 
​
config.headers.common.token = localStorage.getItem('token')
return config 
​
}, err => Promise.reject(err)) 
​
// 响应拦截器封装 
​
request.interceptors.response.use(response => { 
​
// 如果验证未登录,可以跳转到登录页面 
​
if (response.data.code === '10119') { 
​
router.push('/login') 
​
}
​
return response 
​
}, err => Promise.reject(err)) 
​
export default request 

回流和重绘(重排)

回流: 尺寸过布局,隐藏结构变化称为回流

重绘:一些元素影响颜色外观布局为重绘

回流重绘的区别:

回流必将引起重绘,而重绘不一定会引起回流比如:只有颜色改变的时候就只会发生重绘而不会引起回流

引起重绘的原因:

颜色的修改

文本方向的修改

阴影的修改

触发回流

回流触发时机

引起回流的原因

添加或删除可见的DOM元素

元素的位置发生变化

元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)

内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代

页面一开始渲染的时候(这避免不了)

浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

前端验证登陆身份

前台发送登录请求 后台返回 token,前台得到后台返回的 token,将其写入到 localStorage 中,后续请求中都携带 token 后台判断 token 是否过期,如果过期就对前台的请求响应过期标识或者状态码 前台得到过期标识后,清除 localStorage 中的 token,然后重定向到 login 路由

同步异步的区别

异步执行代码: 当代码执行到异步的时候, 先暂时不执行, 继续向后,直到所有的同步代码都执行完毕以后, 再回来执行这个异步代码

同步执行代码: 按照代码从上到下的顺序执行

我们 JS 里面大部分都是同步代码 ( 定时器是一个异步执行的代码 )

预解析,预编译,预处理

JS代码在执行之前,会对代码进行预解析,寻找作用域中的var 和function ,然后对其进行事先声明,在从上到下执行代码。这就是一个预解析的过程。

事件委托

利用事件冒泡的原理,让自己所触发的事件,让它的父元素代替执行

事件冒泡/阻止默认行为

当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window

阻止事件冒泡: 事件对象.stopPropagation()

阻止默认行为: preventDefault(), return falus , a标签内加上javascript

ajax

ajax的事件执行机制(流程底层原理)

一,创建一个异步调用对象

二,创建一个新的http请求

三,设置响应http请求状态变化的函数

四,发送http请求

五,获取异步调用返回的数据

六,使用js和dom实现局部刷新

同步请求(false)

同步请求是指当前发出请求后,浏览器什么都不能做,

必须得等到请求完成返回数据之后,才会执行后续的代码,

相当于生活中的排队,必须等待前一个人完成自己的事物,后一个人才能接着办。

也就是说,当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面处于一个假死状态,

当这个AJAX执行完毕后才会继续运行其他代码页面解除假死状态

什么是异步请求(true)

默认异步:异步请求就当发出请求的同时,浏览器可以继续做任何事,

Ajax发送请求并不会影响页面的加载与用户的操作,相当于是在两条线上,各走各的,互不影响。

一般- 默认值为true,异步。异步请求可以完全不影响用户的体验效果,

无论请求的时间长或者短,用户都在专心的操作页面的其他内容,并不会有等待的感觉。

优点:

· 不需要插件的⽀持,原⽣ js 就可以使⽤

· ⽤户体验好(不需要刷新⻚⾯就可以更新数据)

· 减轻服务端和带宽的负担

缺点:

搜索引擎的⽀持度不够,因为数据都不在⻚⾯上,搜索引擎搜索不到

面向对象 /面向过程

javascript的开发是面向对象

面向对象和面向过程的区别

面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

面向对象的特点儿:

面向对象有三大特性,分别是封装性、继承性和多态性,这里小编不给予太多的解释

面向过程的优缺点

优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

缺点:没有面向对象易维护、易复用、易扩展

面向对象的优缺点

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更

加灵活、更加易于维护

缺点:性能比面向过程低

作用域/作用域链

在 JavaScript 中有两种作用域类型:

  1. 局部作用域:只能在函数内部访问它们,代码在程序的任何地方都能被访问

  2. 全局作用域:网页的所有脚本和函数都能够访问它,在固定的代码片段才能被访问

JavaScript 拥有函数作用域:每个函数创建一个新的作用域。

作用域决定了这些变量的可访问性(可见性)

函数内部定义的变量从函数外部是不可访问的(不可见的)

作用域链:

当查找变量的时候,会先从当前上下文的变量对象中查找,

如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。

这样由多个执行上下文的变量对象构成的链表就叫做作用域链

web性能的优化

减少全局变量, 减少http的请求 , 结构样式行为分离, 路由懒加载, 将脚本放在底部

懒加载简单来说就是延迟加载或按需加载,即在需要的时候的时候进行加载。

路由懒加载

整个网页默认是刚打开就去加载所有页面,路由懒加载就是只加载你当前点击的那个模块,按需去加载路由对应的资源,提高首屏加载速度(tip:首页不用设置懒加载,而且一个页面加载过后再次访问不会重复加载)

路由懒加载的实现原理

将路由相关的组件,不再直接导入了,而是改写成异步组件的写法,只有当函数被调用的时候,才去加载对应的组件内容

为什么要使用路由懒加载

为给客户更好的客户体验,首屏组件加载速度更快一些,解决白屏问题。

懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时

路由懒加载的使用

常用的懒加载方式有两种:即使用vue异步组件 和 ES中的import ​

import Vue from 'vue'
import VueRouter from 'vue-router'
​
Vue.use(VueRouter)
const router = new VueRouter({
 routes: [
    { path: '/login', component: () => import('@/views/login/index.vue') },
    { path: '/home',  component: () => import('@/views/home/home.vue') }
  ]
​
export default router

常用的数组方式

pop() 尾部删除

push() 尾部添加

shift() 头部删除

unshift() 头部添加

array.concat() 连接数组

array.join() 将数组连接起来变成一个字符串

array.reverse() 反转数组

array.slice() 切割数组

array.splice() 截取数组

array.sort() 对数组进行排序

array.toString() 将数组转换成一个字符串

filter() 过滤数组,把原数组满足条件得拿出来,组成一个新的数组

find() 该方法主要应用于查找第一个符合条件的数组元素,即返回通过测试(函数内判断)的数组的第一个元素的值。

改变原数组的数据方法

arr.push()

arr.pop()

arr.unshift()

arr.shift()

arr.splice()

arr.reverse()

arr.sort()

arr.fill()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值