JS面试题

七、Js基础

JavaScript相关

JavaScript分三个部分:

  1. ECMAScript标准—基本语法
  2. DOM—>Document Object Model 文档对象模型,操作页面元素的
  3. BOM—>Browser Object Model 浏览器对象模型,操作浏览器的
    浏览器中有个顶级对象:window----皇上
    页面中顶级对象:document-----总管太监
    页面中所有的内容都是属于浏览器的,页面中的内容也都是window的,因为页面中的所有内容都是window的,window是可以省略的.
    变量是window的
一、数据类型

基本数据类型(值类型): Number、String、Boolean、Undefined、Null、Symbol(es6新增独一无二的值) 和 BigInt(es10新增);

引用数据类型: Object。包含Object、Array、 function、Date、RegExp。

备注: 基本数据类型,又称值类型。

1.基本数据类型和引用数据类型的区别:

基础数据类型,都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问 。
引用数据类型,引用数据类型的值是保存在堆内存中的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。因此,引用类型的值都是按引用访问的。

2.Typeof运算符:

typeof 能有效检测基本类型,检测引用类型都返回object,其中null属于特殊的引用类型返回object,function属于特殊引用类型不用于存储数据,typeof检测返回function.

3.=

===三等表示全等,判断左右两边对象或值是否类型相同且值相等。
二等表示值相等。判断操作符两边对象或值是否相等类型可以不同,类型不同时,使用Number()转换成Number类型在进行判断。例外规则,nullundefined,null/undefined进行运算时不进行隐式类型转换。通常把值转为Boolean值,进行条件判断。Boolean(null)=Boolean(undefined)>false=false 结果为true

4.if语句和逻辑运算

所有基本类型中Boolean值是false的只有6个,分别是 : 0 NaN ’ ’ null undefined false
引用类型Boolean值全是true.
if条件是单个值时,如果是truly值,条件成立, 如果是falsely值,条件不成立

三、类型判断

数据类型判断大概有四种typeof、instanceof、constructor、Object.prototype.toString.call()

二、null和undefined区别

在 if 语句中 null 和 undefined 都会转为false两者用相等运算符比较也是相等
1.null表示没有对象,可能将来要赋值一个对象,即该处不应该有值
1) 作为函数的参数,表示该函数的参数不是对象
2) 作为对象原型链的终点

undefined表示缺少值,即此处应该有值,但没有定义
1)定义了形参,没有传实参,显示undefined
2)对象属性名不存在时,显示undefined
3)函数没有写返回值,即没有写return,拿到的是undefined
4)写了return,但没有赋值,拿到的是undefined

1.Typeof:

不用知道函数返回什么类型,可以使用typeof()定义一个用于接收该函数返回值的变量
基本数据类型中:Number,String,Boolean,undefined 以及引用数据类型中Function ,可以使用typeof检测数据类型,分别返回对应的数据类型小写字符。
另:用typeof检测构造函数创建的Number,String,Boolean都返回object
基本数据类型中:null 。引用数据类型中的:Array,Object,Date,RegExp。不可以用typeof检测。都会返回小写的object

三、类型判断
数据类型判断大概有四种typeof、instanceof、constructor、Object.prototype.toString.call()
1.Typeof:
不用知道函数返回什么类型,可以使用typeof()定义一个用于接收该函数返回值的变量
基本数据类型中:Number,String,Boolean,undefined 以及引用数据类型中Function ,可以使用typeof检测数据类型,分别返回对应的数据类型小写字符。
另:用typeof检测构造函数创建的Number,String,Boolean都返回object
基本数据类型中:null 。引用数据类型中的:Array,Object,Date,RegExp。不可以用typeof检测。都会返回小写的object

2 . A instanceof B

它用来判断这个构造函数的原型是否在给定对象的原型链上。 A是不是B类型 B是否出现在A的圆形脸上

3.constructor

Constructor就是判断是否是一个实例对象,若实例对象上没有实例属性或方法时,就去原型链上寻找

如果对象是undefined或null,则返回相应的“undefined”或“null”。 其他一切都将返回它的类型

4 . 使用Object.prototype.toString.call()检测对象类型

可以通过对象原型上的toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为thisArg。

四、数组方法

1.push() 添加到数组尾部
2.pop() 从数组尾部删除一个元素
3.unshift()添加到数组的头部
4.shift() 从数组头部删除一个元素
5.slice() 从数组中截取

6.splice(1,2,a,b)索引1开始,截取两个加a b

7.reverse() 数组翻转
8.sort() 数组排序
9.join() 数组拼接

  1. filter () 用于过滤数组成员,满足条件的成员组成一个新数组返回;
    11.toString() 数组转字符串
    indexOf() 从前往后遍历,返回item在数组中的索引位,如果没有返回-1;
    lastIndexOf() 与indexOf一样,区别是从后往前找。
    Array.from() 用于类似数组的对象(即有length属性的对象)和可遍历对象转为真正的数组
    every()该方法对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回true。
    map() 将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回
    forEach() forEach方法与map方法很相似,也是对数组的所有成员依次执行参数函数。

reduce map forEach

五、数组去重

1.使用ES6中的set是最简单的去重方法 Array.from(new Set(array));
2.利用Map数据结构去重
创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。
3数组排序再相邻元素比较
4.forEach+indexOf
定义一个空数组存放原数组第一个元素,通过forEach循环,indexOf和原数组对比判断,值是否是-1,如果是push到新的数组中
六、数组排序
1、冒泡排序法
从序列的最右边开始比较相邻两个数字的大小,再根据结果交换两个数字的位置,重复这一操作,实现所有数字从小到大或从大到小排列的算法即冒泡排序。
2、选择排序
从待排序的数据中寻找最小值,将其与序列最左边的数字进行交换,重复这一操作的算法即选择排序。
3、插入排序
从序列左端开始依次对数据进行排序的算法称为插入排序。(序列中数据分为已排序区和未排序区,依次对每个数据和已排序区比较,进行排序)
4、 快速排序
快速排序的原理,简单来说就是把一个事情,分成很多小事情来处理,分治的思想。如找到一个基准,将大于它的放到右边,小于它的所有数放到左边,再分别对左右的数组重复此步骤,最后数组将按照从小到大的顺序排列。
5、 sort排序法
sort是一个方法,该方法内部对该数组对象进行一些操作,又返回来一个数组,不传参数的话排序默认根据字符串的Unicode排序。

七、 数组扁平化

数组扁平化就是将一个多维数组转换为一个一维数组
实现基本方式
1、对数组的每一项进行遍历。
2、判断该项是否是数组。
3、如果该项不是数组则将其直接放进新数组。
4、是数组则回到1,继续迭代。
5、当数组遍历完成,返回这个新数组。

八、字符串

一、for···in和for···of的区别

①从遍历数组角度来说,for···in遍历出来的是key(即下标),for···of遍历出来的是value(即数组的值);
②从遍历字符串的角度来说,同数组一样。
③从遍历对象的角度来说,for···in会遍历出来的为对象的key,但for···of会直接报错。
④如果要使用for…of遍历普通对象,需要配合Object.keys()一起使用。

一、原型和原型链

什么是原型?

在js中,每当定义一个函数数据类型(Object、Function、Arrry、Date等)的时候都会自带一个prototype对象,这个对象就是我们说的原型。

原型又分为显示原型和隐式原型
显示原型是函数里面的prototype属性,每个prototype原型都有一个constructor属性,指向它关联的构造函数。
隐式原型是实例化对象里面的__proto__属性,__proto__属性指向自身构造函数的显示原型prototype属性

什么是原型链

每一个实例化对象都有一个__proto__属性,而这个__proto__属性指向构造函数的原型对象,
原型对象上也有一个__proto__属性,就这样一层一层往上找,直到找到object.phototype,就这样查找的过程就叫原型链

proto prototype constructor的三角关系

函数在声明时会生成一个对象prototype 该对象中有一个constructor指向构造函数本身 当构造函数实例化后,
在实例化对象中会生成一个对象叫__proto__指向构造函数的prototype

person1.constructor === Person

person1.__proto__ === Person.prototype

instance.constructor.prototype = instance.__proto__

二、作用域

作用域就是一个变量可以使用的范围,主要分为全局作用域和函数作用域
全局作用域
在最外层定义的变量或者方法,全局都可以使用,所以是全局作用域。
函数作用域(局部作用域)
js中可以通过函数来创建一个独立作用域称为函数作用域,函数可以嵌套,所以作用域也可以嵌套

ES6的定义的块级作用域
声明变量的方法 有var let const
var 声明的变量可以全局使用,存在变量提升,可以重新赋值
let 声明的变量只能在当前作用域内使用
const 声明的是常量,只能在当前作用域中使用
let/const声明的变量让当前代码块变成一个暂时性的死区
他们声明的变量不存在变量提升,在同一作用域内不能重新赋值

区别: const声明的变量必须给默认值 const声明的是常量不能重新赋值
const声明的变量的值如果是引用数据类型 则数据内部的数据可以修改

变量提升
var声明的变量,function声明的函数存在变量提升
let const 不会变量提升
函数变量的优先级比变量的优先级大
2.自由变量:
假如在全局中定义了变量a,在函数中使用了这个a,这个a就是自由变量,可以这样理解,凡是跨了自己的作用域的变量都叫自由变量。
3.作用域链
自由变量的向上级作用域一层一层查找,直到找到为止,最高找到全局作用域,就形成了作用域链。

三、闭包

闭包:
简单的理解就是函数中套了一个函数,内层函数可以访问外层函数中的变量
闭包就是能够读取其他函数内部变量的函数 还可以作为回调函数,可以实现函数的复用
优点:1.能够读取函数内部的变量 2.让这些变量一直存在于内存中,不会在调用结束后,被垃圾回收机制回收
缺点:闭包会使函数中的变量保存在内存中,内存消耗很大
垃圾回收:
垃圾回收,顾名思义就是释放垃圾占用的空间,防止内存泄露。有效的使用内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。

  • 在JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,只需要对闭包变量null和清除定时器

四、This的指向&改变this指向 call, apply, bind

一、this理解:

​ (1)this是js的一个关键字,指向一个对象;函数中的this指向行为发生的主体,函数外的this指向window

​ (2)函数内的this和函数自身的作用域没有关系,而是取决于函数执行时候的调用主体

​ (3)主体:看函数调用前面有没有点(.),若有则就是其前面的对象,若没有就指向window

​ (4)一般来说,this指向的是哪个最后调用它的对象

​ (5)若函数中有this,但他没有被上一级对象所调用,那么this就指向window;若被上一级对象调用,则this就指向上一级对象

构造函数中的this:

(1)构造函数的new关键字可以改变this的指向,将这个this指向new关键字创建的对象实例

(2)若返回值是一个对象,那么this指向的就是返回的对象,若返回值不是一个对象,那么this还是指向函数的实例

4.箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.,箭头函数的this指向是静态的,声明的时候就确定了下来;

5…Call、apply、bind中

Call、apply可以修改this指向apply第二个参数必须传入的是一个数组,而call 第二个参数可以是任意类型。

obj1.(method).call(obj2,argument1,argument2)

如上,call的作用就是把obj1的方法放到obj2上使用,后面的argument1…这些做为参数传入。

call A.call( B,x,y )

:就是把A的函数放到B中运行,x 和 y 是A方法的参数。 A.call(this) 用call实现继承 this可以继承父类的所有方法和属性

apply(obj,args) obj this指向obj arg 传递数组或者类数组 会作为参数传递给被调用的函数

可以以数组的形式给调用apply方法的函数传递参数

bind方法主要就是将函数绑定到某个对象,bind会创建一个函数,函数体内的this对象的值会被绑定到bind的第一个参数的值,

例如f.bind(obj),实际上可以理解为obj.f(),这时,f函数体内的this自然指向的是obj

五、内存泄漏:

1.循环引用
一个很简单的例子:一个DOM对象被一个接收js对象引用,与此同时又引用同一个或其它的js对象,这个DOM对象可能会引发内存泄露。这个DOM对象的引用将不会在脚本停止的时候被垃圾回收器回收。要想破坏循环引用,需要被赋值为null。
2.闭包
在闭包中引入闭包外部的变量时,当闭包结束时此对象无法被垃圾回收(GC)。
3.DOM泄露
当原有的DOM被移除时,子结点引用没有被移除则无法回收
4.timer 计时器 泄露

六、事件循环EventLoop和消息队列

JavaScript代码的单线程执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。

整个执行过程,我们称为事件循环过程。

一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。

执行宏任务,然后执行该宏任务产生的微任务,若微任务在执行过程中产生了新的微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。

宏任务:setTimeout,setInterval,Ajax,DOM事件
微任务:Promise async/await

img

1、promise中的回调函数立刻执行,then中的回调函数会推入微任务队列中,等待调用栈所有任务执行完才执行

2、async函数里的内容是放入调用栈执行的,await的下一行内容是放入微任务执行的

3、调用栈执行完成后,会不断的轮询微任务队列,即使先将宏任务推入队列,也会先执行微任务

Nodejs中的事件循环

二、手写深拷贝

深拷贝和浅拷贝的区别
1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用
2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”
为什么要使用深拷贝?
我们希望在改变新的数组(对象)的时候,不改变原数组(对象)

arr=[100, [{a : 'hello'}, {b : "world"}], { c: "123456789" }];
    //判断修改的是不是'object'或者null,如果不是 object 或者 null 那么直接返回
    function deepClone(obj = {}) {
        if (typeof obj !== 'object' || obj == null) {
            return obj;
        }
        let result;
        //判断上面下来的obj是不是数组 用instanceof来检测 因为是数组应用类型 
        obj instanceof Array?result=[]:result={}
        for (var item in obj) {
            //查找一个对象是否有某个属性
            if (obj.hasOwnProperty(item)) {
                // 递归调用
                result[item] = deepClone(obj[item])
            }
        }
        return result;
    }
    let arr1=deepClone(arr);
    arr1[1][0].a='vue'
    arr1[0]=99
    console.log('原数组',arr)
    console.log('新数组',arr1)
四、六种常见的继承方式及其优缺点
function Person(name){
    // 给构造函数添加参数
    this.naem = name;
    this.sum = function(){
        alert(this.name)
    }
}
// 给构造函数添加了原型属性
Person.prototype.age = 10
1.原型链继承
function Per(){
	this.name = "ker";
}
Per.prototype = new Person();//主要
var per1 = new Per();
console.log(per1.age);//10
//instanceof 判断元素是否在另一个元素的原型链上l 
//per1继承了Person的属性,返回true
console.log(per1 instanceof Person); //true

父类的实例作为子类的原型 让新实例的原型等于父类的实例。
优点:简单易于实现,父类的新增实例与属性子类都能访问
缺点:
1)所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
2)无法实现多继承
3)创建子类实例时,不能向父类构造函数中传参数

2.借用构造函数继承(伪造对象、经典继承)

用.call()或.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))
优点:
1)解决了子类构造函数向父类构造函数中传递参数
2)可以实现多继承(call或者apply多个父类)
缺点:
1)方法都在构造函数中定义,无法实现构造函数的复用。(每次用每次都要重新调用)
2)不能继承原型属性/方法,只继承了父类构造函数的实例属性,没有继承父类原型的属性。

3)每个新实例都有父类构造函数的副本,臃肿。

3.组合式继承(组合原型链继承和借用构造函数继承)(常用)

调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用

缺点:
由于调用了两次父类,所以产生了两份实例,子类的构造函数会代替原型上的那个父类构造函数。
优点:
1)函数可以复用
2)不存在引用属性问题
3)可以继承属性和方法,并且可以继承原型的属性和方法

4.原型式继承

重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。
特点:类似于复制一个对象,用函数来包装。
缺点:1、所有实例都会继承原型上的属性。
   2、无法实现复用。(新实例属性都是后面添加的)

5.寄生式继承

重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。

6.寄生组合式继承(常用)

寄生:在函数内返回对象然后调用
组合:1、函数的原型等于另一个实例。2、在函数中用apply或者call引入另一个构造函数,可传参

ES5继承和ES6继承的区别:

es5继承首先是在子类中创建自己的this指向,最后将方法添加到this中
Child.prototype=new Parent() || Parent.apply(this) || Parent.call(this)

es6继承是使用关键字先创建父类的实例对象this,最后在子类class中修改this

五、设计模式

一、单例模式

定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点
核心:确保只有一个实例,并提供全局访问
实现:
在vue脚手架项目开发中,我们需要对axios进行请求拦截,响应拦截,多次调用封装好的axios实例也仅设置一次,封装后的axios就是要一个单例

二、观察者、订阅者模式

vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

九、dom操作

DOM是网页中用来表示文档中对象的标准模型,他是由节点和对象组成的结构集合。在浏览器解析HTML标签时,会构建一个DOM树结构,把html结构化成js可以识别的树模型。
树模型构成的层级结构,可以很容易的表明家族成员之间的关系,把复杂的关系简明地表示出来
由此呢js也提供了一些dom的操作

一、dom元素获取
document.getElementById(id的值) 通过id来获取元素的对象,返回值是一个对象
document.getElementsByName(name) 通过name属性来获取对象的,返回值是一个数组,与getElementById()方法类似,但他是查询的name元素,而不是id属性
document.getElementsByTagName() 通过标签来获取元素的对象, 返回值是一个数组
document.getElementsByClassName() 通过class类名来获取的对象,返回值是一个数组
document.querySelector() css选择器,返回与该模式匹配的第一个元素,结果为一个元素;如果没找到匹配的元素,则返回null
document.querySelectorAll() css选择器,返回与该模式匹配的所有元素,结果为一个类数组
二、dom操作
创建:新的标签(元素节点) = document.createElement(“标签名”)
删除:父节点.removeChild(子节点);
插入:insertBefore(新插入的节点,参照物节点) 往某个节点的前面插入一个新的节点
追加:appendChild(新的节点的名) 当前对象追加一个子节点
十、js操作BOM
浏览器对象模型(BOM :Browser Object Model)是JavaScript的组成之一,它提供了独立于内容与浏览器窗口进行交互的对象,使用浏览器对象模型可以实现与HTML的交互。它的作用是将相关的元素组织包装起来,提供给程序设计人员使用,从而降低开发人员的劳动量,提高设计Web页面的能力。

BOM是一个分层结构

浏览器中有个顶级对象:window----皇上
页面中顶级对象:document-----总管太监
页面中所有的内容都是属于浏览器的,页面中的内容也都是window的,因为页面中的所有内容都是window的,window是可以省略的.
变量是window的

十一、事件

一、 事件绑定、事件流、自定义事件
1.DOM事件三种级别:
DOM0级事件
DOM0 级时间分两种,一是直接在标签内直接添加执行语句,二是定义执行函数。

<script>
document.getElementById('button').onclick=function(){									  
	alert(document.getElementById('test').value);
}
</script> 

DOM2 级事件
第一个参数:事件名称
第二个参数:执行函数
第三个参数:指定冒泡还是捕获,默认是false,冒泡。

element.addEventListener('click',function(){},false)

DOM3 级事件
同DOM2级一样,只不过添加了更多的事件类型,鼠标事件、键盘事件
element.addEventListener('keyup',function(){},false)

2.DOM事件两种类型

3.DOM事件的事件流(事件传播)
事件流就是,事件传播过程。
DOM完整的事件流包括三个阶段:事件捕获阶段、目标阶段和事件冒泡阶段。
事件通过捕获到达目标元素,这个时候就是目标阶段。从目标节点元素将事件上传到根节点的过程就是第三个阶段,冒泡阶段。

4.事件捕获的具体流程
当事件发生在 DOM元素上时,该事件并不完全发生在那个元素上。在捕获阶段,事件从window开始,之后是document对象,一直到触发事件的元素。

5.事件冒泡的具体过程
当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在冒泡阶段,事件冒泡,或者事件发生在它的父代,祖父母,直到到达window为止。

6.我们也可以通过 new Event()自定义事件

	var eve = new Event('test'); //通过new Event 创建事件
	dom.addEventListener('test', function () { //注册事件
console.log('test dispatch');});
setTimeout(function () {
dom.dispatchEvent(eve);  //触发事件
}, 1000);

二、事件委托
瀑布流:无限上拉列表中,如果给每一个图片绑定点击事件,非常繁琐且消耗内存。所以我们可以把每张图片上的点击事件委托给共同的父元素。

5.事件冒泡的具体过程
当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在冒泡阶段,事件冒泡,或者事件发生在它的父代,祖父母,直到到达window为止。

6.我们也可以通过 new Event()自定义事件

二、事件委托
瀑布流:无限上拉列表中,如果给每一个图片绑定点击事件,非常繁琐且消耗内存。所以我们可以把每张图片上的点击事件委托给共同的父元素。

事件委托,又名事件代理。事件委托就是利用事件冒泡,就是把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托也就没法实现了

好处:提高性能,减少了事件绑定,从而减少内存占用

应用场景 在vue中事件委托
我们经常遇到vue中v-for一个列表,列表的每一项都绑定了@click处理事件。我们都知道绑定这么多监听,从性能方面来说是不太好的。那我们我们可以通过把每个item的click事件委托给父元素的形式来实现

三、封装一个通用的事件绑定函数
需要点击每个a,来。弹出他们的内容

<div id="div3">
    <a href="#">a1</a><br>
    <a href="#">a2</a><br>
    <a href="#">a3</a><br>
    <a href="#">a4</a><br>
    <button id='btn1'>加载更多...</button>
</div>
<script>
// 封装通用的事件绑定函数
function bindEvent(elem, type, fn) {
    elem.addEventListener(type, fn)
}
//获取父元素
const fu = document.getElementById('div3')
bindEvent(fu, 'click', function (event) {
    // console.log(event.target) // 获取触发的元素
    let target=event.target
    event.preventDefault() // 阻止默认行为
    //过滤符合条件的子元素,主要是过滤掉 加载更多 
    if(target.nodeName.toLowerCase()==="A"){
        alert(target.innerHTML;
    }
})

十二、Ajax

一、原生Ajax的创建过程
1.创建xhr 核心对象
var xhr=new XMLHttpRequest();

十二、Ajax
一、原生Ajax的创建过程
1.创建xhr 核心对象
var xhr=new XMLHttpRequest();

2.调用open 准备发送
参数一:请求方式
参数二: 请求地址
参数三:true异步,false 同步
xhr.open(‘post’,‘http://www.baidu.com/api/search’,true)

3.如果是post请求,必须设置请求头。
xhr.setRequestHeader(‘Content-Type’, ‘application/x-www-form-urlencoded’)

4.调用send 发送请求 (如果不需要参数,就写null)
xhr.send(‘user=tom&age=10&sex=女’)

5.监听异步回调 onreadystatechange
判断readyState 为4 表示请求完成
判断status 状态码 为 200 表示接口请求成功
responeseText 为相应数据。字符串类型。

xhr.onreadystatechange=function(){
	if(xhr.readyState==4){ 
		if(xhr.status==200){
              console.log(xhr.responseText);
              var res=JSON.parse(xhr.responseText);
              console.log(res);
              if(res.code==1){
                modal.modal('hide');
                location.reload();
              }
            }
          }

备注:如果是post请求,想要传json格式数据。
设置请求头

1.xhr.setRequestHeader(‘Content-Type’, ‘application/json’)

open发送数据
2.xhr.open({_id:xxx,user:xxxx,age:xxxx})

二、Jsonp的原理

JSONP原理:

ajax 请求受同源策略影响,不允许进行请求,我们利用 script 标签的 src 属性不受同源策略的约束,利用这个特性jsonp需要以下步骤:

1.动态创建添加到页面中执行 (body.appendChild(‘script’))
4.页面要提前定义好callback。
5.后端会返回回调函数执行并包裹参数callback(data)
备注:
服务端不再返回JSON格式的数据,而是返回回调函数包裹数据(fn({name:‘tom’,age:18}),在src中进行了调用,这样实现了跨域。

十三、存储cookie、localStorage、sessionStorage,

本地存储分为cookie、localStorage、sessionStorage,
Cookie
Cookie设计初衷是用来和服务器通讯,而不是本地存储, 前后端都可以进行操作 cookie。
优点:可设置失效时间,没有设置的话,默认是关闭浏览器后失效,每次都会自动携带在HTTP头中,
缺点:存储空间大小只有4KB左右 如果使用cookie保存过多数据会带来性能问题

localStorage、sessionStorage主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题
localStorage、sessionStorage优点:
localStorage和sessionStorage:可以保存5MB的信息。
localStorage和sessionStorage:仅在客户端(即浏览器)中保存,不参与和服务器的通信

localStorage、sessionStorage缺点:
localStorage:除非被手动清除,否则将会永久保存。
sessionStorage: 仅在当前网页会话下有效,关闭页面或浏览器后就会被清除。
localStorage可以用来跨页面传递参数,sessionStorage用来保存一些临时的数据,防止用户刷新页面之后丢失了参数。他们两个都是H5才应用的新特性
API有哪些
setItem(存储) getItem(取出) clear(删除所有) removeItem(删除某一个) JSON.stringify()用于从一个对象解析出字符串;JSON.parse()用于从一个字符串中解析出json对象。

注意:localStorage只支持string类型的存储。

Es6相关

一、 let、var、const区别

在ES5中,声明变量只有var和function两种形式。但是因为var声明的变量会有一定的缺点(内层变量可能覆盖外层变量的问题以及用来计数的循环变量泄露为全局变量,下面有介绍),ES6提出了使用let和const声明变量,弥补了ES5中var的缺点。

1.是否存在变量提升?
var声明的变量存在变量提升(将变量提升到当前作用域的顶部)。即变量可以在声明之前调用,值为undefined。

let和const不存在变量提升。即它们所声明的变量一定要在声明后使用,否则报ReferenceError错。
2.是否存在暂时性死区?
let和const存在暂时性死区。即只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

3.是否允许重复声明变量?
var允许重复声明变量。
let和const在同一作用域不允许重复声明变量。

二、es6解构赋值

解构赋值就是从目标对象或数组中提取自己想要的变量。最常用的场景是:element-ui,vant-ui按需引入,请求接口返回数据,提取想要数据。

常见的几种方式有
1.函数参数默认值
2.交换变量

let a=1, b=2;
[b, a] = [a, b]
console.log(a, b)

3.将剩余数组赋给一个变量
当结构一个数组时,可以使用剩余模式,将数组剩余部分赋值给一个变量
4.给新的变量名赋值
可以从一个对象中提取变量并赋值给和对象属性名不同的新的变量名

三、箭头函数与普通函数的区别

1、箭头函数是匿名函数,不能作为构造函数,不能使用new
箭头函数相当于匿名函数,并且简化了函数定义。箭头函数有两种格式:一种只包含一个表达式,连{ … }和return都省略掉。还有一种可以包含多条语句,这时候就不能省略{ … }和return。
2.箭头函数内没有arguments,可以用展开运算符…解决
arguments:是一个方法调用的集合,是一个伪数组,不是真的数组,不具有数组的操作的方法,可以用展开运算解决(…)
3.箭头函数的this,始终指向父级上下文(箭头函数的this取决于定义位置父级的上下文,跟使用位置没关系,普通函数this指向调用的那个对象)
4.箭头函数不能通过call()、apply()、bind()方法直接修改它的this指向。
5.箭头函数没有原型属性

Set、Map的区别

应用场景Set用于数据重组,Map用于数据储存

Set:
1,成员不能重复
2,只有键值没有键名,类似数组
3,可以遍历,方法有add, delete,has

Map:
1,本质上是健值对的集合,类似集合
2,可以遍历,可以跟各种数据格式转换

四、class与class继承

传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。

ES5中如果要生成一个对象实例,需要先定义一个构造函数,然后通过new操作符来完成。
构造函数生成实例的执行过程:
1.当使用了构造函数,并且new 构造函数(),后台会隐式执行new Object()创建对象;
2.将构造函数的作用域给新对象,(即new Object()创建出的对象),而函数体内的this就代表new Object()出来的对象。
3.执行构造函数的代码。
4.返回新对象(后台直接返回);

ES6中的类

ES6引入了class(类)这个概念,通过class关键字可以定义类。该关键字的出现使得javascript在对象写法上更加清晰,更像是一种面向对象的语言。
注意项:
1.在类中声明方法的时候,千万不要给该方法加上function关键字
2.方法之间不要用逗号分隔,否则会报错

  1. 类中的构造器constructor不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
  2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
  3. 类中所定义的方法,都放在了类的原型对象上,供实例去使用。

statc 函数的静态成员 类的静态属性 是属于自身的 其创建的实例对象无法使用

class Person {
    static eat() {
        console.log('eat');
    }
}
let  p = new Person();
Person.eat(); // eat
p.eat(); // 报错

super()相当于Parent.prototype.constructor.call(this)

十四、new创建新实例对象经过了以下几步:

1.创建一个新对象
2.将新对象的__proto__指向构造函数的prototype对象
3.将构造函数的作用域赋值给新对象 (也就是this指向新对象)
4.执行构造函数中的代码(为这个新对象添加属性)
5.返回新的对象

五、*promise使用及实现

1.什么是promise

Promise,是JS中进行异步编程的新的解决方案
从语法上说,

Promise是一个构造函数 (自己身上有allrejectresolve这几个方法,原型上有thencatch等方法)

promise对象用来封装一个异步操作并可以获取其成功/失败的结果值

2. Promise 的状态

实例对象promise中的一个属性 PromiseState

pending 变为 resolved/fullfilled
pending 变为 rejected
注意

对象的状态不受外界影响
只有这两种,且一个 promise 对象只能改变一次
一旦状态改变,就不会再变,任何时候都可以得到这个结果
无论成功还是失败,都会有一个结果数据。成功的结果数据一般称为 value,而失败的一般称为 reason。

使用 new 来创建一个promise对象。
Promise接受一个「函数」作为参数,该函数的两个参数分别是resolve和reject。这两个generator函数就是就是「回调函数」

Resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去

then()方法: then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
catch()方法: 当执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么也不会报错卡死 js,而是会进到这个 catch 方法中。
all()方法: Promise 的 all 方法提供了并执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
race()方法:race 的用法与 all 一样,只不过 all 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。

6.Promise 异常穿透(传透)?

(1)当使用 promisethen 链式调用时,可以在最后指定失败的回调

(2)前面任何操作出了异常,都会传到最后失败的回调中处理

7.中断 promise 链?

当使用 promisethen 链式调用时,在中间中断,不再调用后面的回调函数

办法:在回调函数中返回一个 pending 状态的 promise 对象

六、*async await

async :是“异步”的简写, async 用于申明一个异步的 function
await:await 用于等待一个异步方法执行完成。
特点:
asayc的用法,它作为一个关键字放到函数前面,这样普通函数就变为了异步函数
异步async函数调用,跟普通函数的使用方式一样
异步async函数返回一个promise对象
async函数配合await关键字使用(阻塞代码往下执行)是异步方法,但是阻塞式的

async/await是建立在 Promises上的,不能被使用在普通回调以及节点回调写法更加优雅,
promise是成熟的方案,async/await是es7的最终解决异步方案 同步的写法,书写异步的操作
async await就是generator语法糖,所谓语法糖按照我自己理解它就是一种方式,你使用别的方法也可以达到目的,使用语法糖会更方便更快

迭代器Iterator是一种接口,

为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口就可以完成遍历操作(接口在js中就是对象的属性)

  • ES6有for(let [k,v] of map)遍历Map数据结构中的键和值 for in 取的是索引

  • 具备Iterator的的数据

    • Array Arguments Set Map String NodeList

    • 原理 创建一个指针next()(js中的指针不同与C,仅仅只是下标索引)调用next方法,指针自动指向数据结构的下一个成员,直至最后一个成员。

      每次调用next()方法会返回一个包含value和done属性的对象

七、generator函数

​ 函数在前面加了一个*就变成了generator函数,Es6异步编程解决方案

并且只有在generator函数中才能使用yield,什么是yield呢,他相当于generator函数执行的中途暂停点。

而怎么才能暂停后继续走呢?那就得使用到next方法,next方法执行后会返回一个对象,对象中有value 和 done两个属性

value:暂停点后面接的值,也就是yield后面接的值
done:是否generator函数已走完,没走完为false,走完为true

八、Es6中新的数据类型symbol

Symbol 值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值