前端Js笔试题面试题

前言

为了方便如今和之后前端学习和面试,在此收集和整理了Js相关的笔试面试题,供本身查阅的同时,但愿也会对你们有所帮助。javascript

目录:

前端HTML+CSS笔试题面试题
前端JS笔试题面试题
前端Vue笔试题面试题
前端小程序笔试题面试题html

数据类型

JS的基本数据类型

Undefined、Null、Boolean、Number、String新增:Symbol前端

JS有哪些内置对象?

ObjectJavaScript 中全部对象的父对象
数据封装类对象:Object、Array、Boolean、NumberString
其余对象:Function、Arguments、Math、Date、RegExp、Errorjava

JS中使用typeof能获得的哪些类型?(考点:JS变量类型)

typeof undefinded //undefined
typeof null // object
typeof'abc' // string
typeof 123 // number
typeof [] // object
typeof {} // object
typeof true //boolean
typeof b // b 没有声明,可是还会显示 undefined
复制代码

什么时候使用===什么时候使用==?(考点:强制类型转换)

== 比较两个值相等返回ture
=== 比较二者的值和类型都相等才返回true 严格相等
0,NAN,null,undefinded,'',falseif语句中会强制转化为 falsenode

if (obj.a == null) {
    // 这里至关于 obj.a === null || obj.a ===undefinded, 简写形式
    // 这里jquery 源码中推荐的写法
}
复制代码

null,undefined 的区别?

一、nullNull类型,表明“空值”,表明一个空对象指针,使用typeof运算获得 “object”,因此你能够认为它是一个特殊的对象值。
二、undefinedUndefined类型,当一个声明了一个变量未初始化时,获得的就是undefinedjquery

ps:一句话简单来讲nullundefine值比较是相等,但类型不一样。nginx

Javascript建立对象的几种方式?

一、对象字面量的方式面试

var = {}
复制代码

二、经过构造函数方式建立。ajax

var obj = new Object(); 
复制代码

三、经过Object.create()方式建立。算法

var obj = Object.create(Object.prototype);
复制代码

翻转一个字符串

能够先将字符串转成一个数组,而后用数组的reverse()+join()方法。

let str="hello word";
let b=[...str].reverse().join("");//drow olleh
复制代码

描述new一个对象的过程

一、建立一个新对象
二、this指向这个对象
三、执行代码,即对this赋值
四、隐式返回this

JS按存储方式区分变量类型

// 值类型
var a = 10
var b = a
a = 11
console.log(b) // 10

// 引用类型
var obj1 = {x : 100}
var obj2 = obj1
obj1.x = 200
console.log(obj2) // 200
复制代码

如何判断一个变量是对象仍是数组?

一、instanceof方法 instanceof运算符是用来测试一个对象是否在其原型链原型构造函数的属性

var arr = []; 
arr instanceof Array; // true
复制代码

二、constructor方法 constructor属性返回对建立此对象的数组函数的引用,就是返回对象相对应的构造函数

var arr = []; 
arr.constructor == Array; //true
复制代码

三、最简单的方法,这种写法兼容性最好使用Object.prototype.toString.call()

function isObjArr(value){
     if (Object.prototype.toString.call(value) === "[object Array]") {
            console.log('value是数组');
       }else if(Object.prototype.toString.call(value)==='[object Object]'){//这个方法兼容性好一点
            console.log('value是对象');
      }else{
          console.log('value不是数组也不是对象')
      }
}
复制代码

四、ES5新增方法isArray()

var a = new Array(123);
var b = new Date();
console.log(Array.isArray(a)); //true
console.log(Array.isArray(b)); //false
复制代码

ps:千万不能使用typeof来判断对象和数组,由于这两种类型都会返回"object"

如何对一个数组去重?

一、Set结构去重(ES6用法)。
ES6提供了新的数据结构Set。它相似于数组,可是成员的值都是惟一的,没有重复的值。

[...new Set(array)];
复制代码

二、遍历,将值添加到新数组,用indexOf()判断值是否存在,已存在就不添加,达到去重效果。

let a = ['1','2','3',1,NaN,NaN,undefined,undefined,null,null, 'a','b','b'];
    let unique= arr =>{
         let newA=[];
        arr.forEach(key => {
           if( newA.indexOf(key)<0 ){ //遍历newA是否存在key,若是存在key会大于0就跳过push的那一步
             newA.push(key);
           }
        });
        return newA;
    }
    console.log(unique(a)) ;//["1", "2", "3", 1, NaN, NaN, undefined, null, "a", "b"]
// 这个方法不能分辨NaN,会出现两个NaN。是有问题的,下面那个方法好一点。
复制代码

三、利用for嵌套for,而后splice去重(ES5中最经常使用)。

function unique(arr){            
        for(var i=0; i<arr.length; i++){
            for(var j=i+1; j<arr.length; j++){
                if(arr[i]==arr[j]){         //第一个等同于第二个,splice方法删除第二个
                    arr.splice(j,1);
                    j--;
                }
            }
        }
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     //NaN和{}没有去重,两个null直接消失了
复制代码

四、forEach遍历,而后利用Object.keys(对象)返回这个对象可枚举属性组成的数组,这个数组就是去重后的数组。

let a = ['1', '2', '3', 1,NaN,NaN,undefined,undefined,null,null, 'a', 'b', 'b'];
    const unique = arr => {
        var obj = {}
        arr.forEach(value => {
            obj[value] = 0;//这步新添加一个属性,并赋值,若是不赋值的话,属性会添加不上去
        })
        return Object.keys(obj);//`Object.keys(对象)`返回这个对象可枚举属性组成的数组,这个数组就是去重后的数组
    }
    console.log(unique(a));//["1", "2", "3", "NaN", "undefined", "null", "a", "b"]
    
复制代码

做用域和闭包

var、let、const之间的区别

var声明变量能够重复声明,而let不能够重复声明
var是不受限于块级的,而let是受限于块级
var会与window相映射(会挂一个属性),而let不与window相映射
var能够在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
const声明以后必须赋值,不然会报错
const定义不可变的量,改变了就会报错
constlet同样不会与window相映射、支持块级做用域、在声明的上面访问变量会报错

说明This几种不一样的使用场景

一、做为构造函数执行
二、做为对象属性执行
三、做为普通函数执行
四、call apply bind

谈谈This对象的理解

一、this老是指向函数的直接调用者(而非间接调用者)
二、若是有new关键字,this指向new出来的那个对象
三、在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this老是指向全局对象Window

做用域

ES5做用域分为 全局做用域 和 函数做用域。
ES6新增块级做用域,块做用域由 { }包括,if语句和 for语句里面的{ }也属于块做用域。

// 块级做用域
if (true) {
    var name = 'zhangsan'
}
console.log(name)
// 函数和全局做用域
var a = 100
function fn () {
    var a = 200
    console.log('fn', a)
}
复制代码

JS建立10个<a>标签,点击的时候弹出来对应的序号?(考点:做用域)

var i 
for (i = 0; i < 10; i++) {
    (function (i) {
        var a = document.createElement('a')
        a.innerHTML = i + '<br>'
        a.addEventListener('click', function (e) {
            e.preventDefault()
            alert(i)
        })
        document.body.appendChild(a)
    })(i)
}
复制代码

说说你对做用域链的理解

一、做用域链的做用是保证执行环境里有权访问的变量和函数是有序的,做用域链的变量只能向上访问,变量访问到window对象即被终止,做用域链向下访问变量是不被容许的
二、简单的说,做用域就是变量与函数的可访问范围,即做用域控制着变量与函数的可见性和生命周期
三、通俗来讲,通常状况下,变量取值到 建立 这个变量 的函数的做用域中取值。 可是若是在当前做用域中没有查到值,就会向上级做用域去查,直到查到全局做用域,这么一个查找过程造成的链条就叫作做用域链

var a = 100
function fn () {
    var b = 200
    // 当前做用域没有定于的变量,即‘自由变量’
    console.log(a)
    console.log(b)
}
fn()
console.log(a) 去父级做用域找a自由变量 做用域链
复制代码

闭包

闭包就是可以读取其余函数内部变量的函数
闭包是指有权访问另外一个函数做用域中变量的函数,建立闭包的最多见的方式就是在一个函数内建立另外一个函数,经过另外一个函数访问这个函数的局部变量,利用闭包能够突破做用链域

function F1 () {
    var a = 100
    // 返回一个函数,函数做为返回值
    return function () {
        console.log(a)
    }
}
// f1 获得一个函数
var f1 = F1()
var a = 200
f1()  // 100 定义的时候父级做用域
复制代码

闭包的特性

函数内再嵌套函数
内部函数能够引用外层的参数和变量
参数和变量不会被垃圾回收机制回收

说说你对闭包的理解

使用闭包主要是为了设计私有的方法和变量。闭包的优势是能够避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易形成内存泄露。在js中,函数即闭包,只有函数才会产生做用域的概念

闭包的最大用处有两个,一个是能够读取函数内部的变量,另外一个就是让这些变量始终保持在内存中
闭包的另外一个用处,是封装对象的私有属性和私有方法
好处:可以实现封装和缓存等;
坏处:就是消耗内存、不正当使用会形成内存溢出的问题

使用闭包的注意点

因为闭包会使得函数中的变量都被保存在内存中,内存消耗很大,因此不能滥用闭包,不然会形成网页的性能问题,在IE中可能致使内存泄露 解决方法是,在退出函数以前,将不使用的局部变量所有删除

闭包的使用场景

闭包的两个场景:函数做为返回值 和 函数做为参数传递

function F1 () {
    var a = 100
    // 返回一个函数,函数做为返回值
    return function () {
        console.log(a)
    }
}
// f1 获得一个函数
var f1 = F1()
var a = 200
f1()  // 100 定义的时候父级做用域

function F2 (fn) {
    var a = 200
    fn()
}
F2(f1)
复制代码

实际开发中闭包的应用

// 闭包实际应用中主要用于封装变量 收敛权限
function isFirstLoad() {
    var _list = []
    return function (id) {
        if (_list.indexOf(id) >= 0) {
            return false
        } else {
            _list.push(id)
            return true
        }
    }
}
// 使用
var firstLoad = new isFirstLoad()
firstLoad(10) //true
firstLoad(10) //false
firstLoad(30) //true
复制代码

原型和原型链

JavaScript原型,原型链 ? 有什么特色?

每一个对象都会在其内部初始化一个属性,就是prototype(原型), 当咱们访问一个对象的属性时,若是这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有本身的prototype,因而就这样一直找下去,也就是咱们平时所说的原型链的概念

原型和原型链关系

关系:instance.constructor.prototype = instance.__proto__

原型和原型链特色

JavaScript对象是经过引用来传递的,咱们建立的每一个新对象实体中并无一份属于本身的原型副本。当咱们修改原型时,与之相关的对象也会继承这一改变 当咱们须要一个属性的时,Javascript引擎会先看当前对象中是否有这个属性, 若是没有的 就会查找他的Prototype对象是否有这个属性,如此递推下去,一直检索到 Object 内建对象
PS: 1.全部的引用类型(数组,对象,函数)都具备对象的特性,便可自由扩展属性(除了‘null’)除外
2.全部的引用类型(数组,对象,函数)都有一个_proto_(隐式原型)属性,属性值是一个普通对象
3.全部的函数,都有一个prototype(显示原型)的属性,也是一个普通对象
4.全部的引用类型(数组,对象,函数)_proto_属性值指向他的构造的‘prototype’ 属性值

当试图获得一个对象的某个属性时,若是这个对象自己没有这个属性,那么会去它的_proto_(即它的构造函数的prototype) 中寻找

// 构造函数
function Foo(name, age){
    this.name = name 
}
Foo.prototype.alertName = function () {
    alert(this.name)
}
//建立实例
var f = new Foo('zhangsan')
f.printName = function () {
    console.log(this.name)
}
//测试
f.printName()
f.alertName()
复制代码

循环对象自身的属性

var item
for (item in f) {
    // 高级浏览器已经在 for in 中屏蔽了来自原型的属性
    // 可是这里建议你们仍是加上这个判断,保证程序的健壮性
    if (f.hasOwnProperty(item)) {
        console.log(item)
    }
}
复制代码

原型链

f.toString() //到f._proto_._proto_ 中去找
复制代码

异步和单线程

同步和异步的区别是什么?分别举一个同步和异步的例子

同步交互:指发送一个请求,须要等待返回,而后才可以发送下一个请求,有个等待过程。
异步交互:指发送一个请求,不须要等待返回,随时能够再发送下一个请求,即不须要等待。
相同的地方:都属于交互方式,都是发送请求。
不一样的地方:一个须要等待,一个不须要等待。
简单而言,同步就是必须一件一件的作事,等前一件事作完后才能作下一件事。而异步这是把事情指派给别人后,接着继续作下一件事,没必要等别人返回的结果。
举例:
一、广播,就是一个异步例子。发起者不关心接收者的状态。不须要等待接收者的返回信息; 在部分状况下,咱们的项目开发中都会优先选择不须要等待的异步交互方式。
二、电话,就是一个同步例子。发起者须要等待接收者,接通电话后,通讯才开始。须要等待接收者的返回信息 好比银行的转帐系统,对数据库的保存操做等等,都会使用同步交互操做。
ps: alert是同步,setTimeoutsetInterval是异步,同步会阻塞代码执行,而异步不会

异步编程的实现方式?

一、回调函数
优势:简单、容易理解
缺点:不利于维护,代码耦合高

二、事件监听(采用时间驱动模式,取决于某个事件是否发生):
优势:容易理解,能够绑定多个事件,每一个事件能够指定多个回调函数
缺点:事件驱动型,流程不够清晰

三、发布/订阅(观察者模式)
相似于事件监听,可是能够经过‘消息中心’,了解如今有多少发布者,多少订阅者

四、Promise对象
优势:能够利用then方法,进行链式写法;能够书写错误时的回调函数;
缺点:编写和理解,相对比较难

五、Generator函数
优势:函数体内外的数据交换、错误处理机制
缺点:流程管理不方便

六、async函数
优势:内置执行器、更好的语义、更广的适用性、返回的是Promise、结构清晰。
缺点:错误处理机制

定时器的执行顺序或机制。

简单来讲:由于js是单线程的,浏览器遇到setTimeout或者setInterval会先执行完当前的代码块,在此以前会把定时器推入浏览器的待执行事件队列里面,等到浏览器执行完当前代码以后会看一下事件队列里面有没有任务,有的话才执行定时器的代码。 因此即便把定时器的时间设置为0仍是会先执行当前的一些代码。

一个关于setTimeout的笔试题

console.log(1)
setTimeout(function () {
    console.log(2)
},0)
console.log(3)
setTimeout(function () {
console.log(4)
},1000)
console.log(5)
//结果为 1 3 5 2 4
复制代码

前段使用异步的场景

一、定时任务:setTimeout,setInverval
二、网络请求:ajax请求,动态<img>加载
三、事件绑定

数组和对象API

map与forEach的区别?

一、forEach方法,是最基本的方法,就是遍历与循环,默认有3个传参:分别是遍历的数组内容item、数组索引index、和当前遍历数组Array
二、map方法,基本用法与forEach一致,可是不一样的,它会返回一个新的数组,因此在callback须要有return值,若是没有,会返回undefined

JS 数组和对象的遍历方式,以及几种方式的比较

一般咱们会用循环的方式来遍历数组。可是循环是 致使js 性能问题的缘由之一。通常咱们会采用下几种方式来进行数组的遍历
for in循环
for循环
forEach
一、这里的 forEach回调中两个参数分别为 value,index
二、forEach 没法遍历对象
三、IE不支持该方法;Firefoxchrome 支持
四、forEach 没法使用 break,continue 跳出循环,且使用 return 是跳过本次循环
五、能够添加第二个参数,为一个数组,回调中的this会指向这个数组,若没有添加,则是指向window
在方式一中for-in须要分析出array的每一个属性,这个操做性能开销很大。用在 key 已知的数组上是很是不划算的。因此尽可能不要用for-in,除非你不清楚要处理哪些属性,例如JSON对象这样的状况
在方式2中,循环每进行一次,就要检查一下数组长度。读取属性(数组长度)要比读局部变量慢,尤为是当array里存放的都是 DOM元素,由于每次读取都会扫描一遍页面上的选择器相关元素,速度会大大下降

写一个能遍历对象和数组的通用forEach函数

function forEach(obj, fn) {
    var key
    // 判断类型
    if (obj instanceof Array) {
        obj.forEach(function (item, index) {
            fn(index, item)
        })
    } else {
        for (key in obj) {
            fn ( key, obj[key])
        }
    }
}
复制代码

数组API

map: 遍历数组,返回回调返回值组成的新数组
forEach: 没法break,能够用try/catch中throw new Error来中止
filter: 过滤
some: 有一项返回true,则总体为true
every: 有一项返回false,则总体为false
join: 经过指定链接符生成字符串
push / pop: 末尾推入和弹出,改变原数组, 返回推入/弹出项【有误】
unshift / shift: 头部推入和弹出,改变原数组,返回操做项【有误】
sort(fn) / reverse: 排序与反转,改变原数组
concat: 链接数组,不影响原数组, 浅拷贝
slice(start, end): 返回截断后的新数组,不改变原数组
splice(start, number, value...): 返回删除元素组成的数组,value 为插入项,改变原数组
indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标
reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)

复制代码

对象API

var obj = {
    x: 100,
    y: 200,
    z: 300
} 
var key
for (key in obj) {
    // 注意这里的 hasOwnProperty,再讲原型链的时候讲过
    if (obj.hasOwnProperty(key)) {
        console.log(key, obj[key])
    }
}
复制代码

日期和随机数

获取2020-06-10格式的日期

Date.now() // 获取当前时间毫秒数
var dt = new Date()
dt.getTime() //获取毫秒数
dt.getFullYear() // 年
dt.getMonth() // 月 (0-11)
dt.getDate() // 日 (0-31)
dt.getHours() // 小时 (0-23)
dt.getMinutes() //分钟 (0-59)
dt.getSeconds() //秒 (0-59)
复制代码

ps: ES6 新增padStart(),padEnd()方法可用来返回06格式日期

获取随机数,要求是长度一致的字符串格式

var random = Math.random()
var random = random + '0000000000'
console.log(random)
var random = random.slice(0, 10)
console.log(random)
复制代码

DOMBOM操做

DOM是哪一种基本的数据结构

DOM是一种树形结构的数据结构

DOM操做的经常使用API有哪些

一、获取DOM节点,以及节点的propertyAttribute
二、获取父节点,获取子节点
三、新增节点,删除节点

DOM节点的Attributeproperty有何区别

property只是一个JS对象的属性修改和获取
Attribute是对html标签属性的修改和获取

DOM的节点操做

建立新节点

createDocumentFragment()    //建立一个DOM片断
createElement()   //建立一个具体的元素
createTextNode()   //建立一个文本节点
复制代码

添加、移除、替换、插入

appendChild()      //添加
removeChild()      //移除
replaceChild()      //替换
insertBefore()      //插入
复制代码

查找

getElementsByTagName()    //经过标签名称
getElementsByName()     //经过元素的Name属性的值
getElementById()        //经过元素Id,惟一性
复制代码

如何检测浏览器的类型
能够经过检测navigator.userAgent 在经过不通浏览器的不通来检测

var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)
复制代码

拆解url的各部分
使用location里面的location.href location.protocol location.pathname location.search location.hash来获取各类参数

console.log(location.href)
console.log(location.host) //域名
console.log(location.protocol)
console.log(location.pathname) //路径
console.log(location.search) // 参数
console.log(location.hash) 

// history
history.back()
history.forward()
复制代码

事件机制

请解释什么是事件代理

事件代理(Event Delegation),又称之为事件委托。是 JavaScript 中经常使用绑定事件的经常使用技巧。顾名思义,“事件代理”便是把本来须要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。

使用事件代理的好处是:

一、能够提升性能
二、能够大量节省内存占用,减小事件注册,好比在table上代理全部tdclick事件就很是棒
三、能够实现当新增子对象时无需再次对其绑定

事件模型

W3C中定义事件的发生经历三个阶段:捕获阶段(capturing)、目标阶段(targetin)、冒泡阶段(bubbling)

冒泡型事件:当你使用事件冒泡时,子级元素先触发,父级元素后触发
捕获型事件:当你使用事件捕获时,父级元素先触发,子级元素后触发
DOM事件流:<时支持两种事件模型:捕获型事件和冒泡型事件
阻止冒泡:W3c中,使用stopPropagation()方法;在IE下设置cancelBubble = true
阻止捕获:阻止事件的默认行为,例如click - <a>后的跳转。在W3c中,使用preventDefault()方法,在IE下设置window.event.returnValue = false

编写一个通用的事件监听函数

function bindEvent(elem,type,selector,fn){
    if(fn==null){
        fn = selector;
        selector = null
    }
    elem.addEventListener(type,function(e){
        var target;
        if(selector){
            target = e.target;
            if(target.matches(selector)){
                fn.call(target,e)
            }
        }else{
            fn(e)
        }
    })
}
复制代码

描述事件冒泡流程

当给某元素绑定一个事件的时候,首先会触发本身绑定的,而后会逐层向上级查找事件,这就是事件冒泡

var p1 = document.getElementById('p1')
        var body = document.body
        function bindEvent(elem, type, fn) {
            elem.addEventListener(type, fn)
        } 
        bindEvent(p1, 'click', function (e) {
                e.stopPropagation()
                var target = e.target
                alert("激活")
            })
            bindEvent(body, 'click', function (e) {
                var target = e.target
                alert("取消")
            })
复制代码

对于一个无限下拉加载图片的页面,如何给每一个图片绑定事件

可使用代理,经过对父级元素绑定一个事件,经过判断事件的target属性来进行判断,添加行为

var div1 = document.getElementById('div1')
        var div2 = document.getElementById('div2')
        function bindEvent(elem, type, fn) {
            elem.addEventListener(type, fn)
        } 
        bindEvent(div1, 'click', function (e) {
                var target = e.target
                alert(target.innerHTML)
            })
        bindEvent(div2, 'click', function (e) {
                var target = e.target
                alert(target.innerHTML)
        })
复制代码

AJAX

Ajax原理

Ajax的原理简单来讲是在用户和服务器之间加了—个中间层(AJAX引擎),经过XmlHttpRequest对象来向服务器发异步请求,从服务器得到数据,而后用javascript来操做DOM而更新页面。使用户操做与服务器响应异步化。这其中最关键的一步就是从服务器得到请求数据
Ajax的过程只涉及JavaScript、XMLHttpRequest和DOMXMLHttpRequest是ajax的核心机制

手动编写一个ajax,不依赖第三方库

// 1. 建立链接
    var xhr = null;
    xhr = new XMLHttpRequest()
    // 2. 链接服务器
    xhr.open('get', url, true)
    // 3. 发送请求
    xhr.send(null);
    // 4. 接受请求
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4){
            if(xhr.status == 200){
                success(xhr.responseText);
            } else { // fail
                fail && fail(xhr.status);
            }
        }
    }
复制代码

ajax的优势和缺点

ajax的优势

一、无刷新更新数据(在不刷新整个页面的状况下维持与服务器通讯)
二、异步与服务器通讯(使用异步的方式与服务器通讯,不打断用户的操做)
三、前端和后端负载均衡(将一些后端的工做交给前端,减小服务器与宽度的负担)
四、界面和应用相分离(ajax将界面和应用分离也就是数据与呈现相分离)

ajax的缺点

一、ajax不支持浏览器back按钮
二、安全问题 Aajax暴露了与服务器交互的细节
三、对搜索引擎的支持比较弱
四、破坏了BackHistory后退按钮的正常行为等浏览器机制。

什么状况下会碰到跨域问题?有哪些解决方法?

跨域问题是这是浏览器为了安全实施的同源策略致使的,同源策略限制了来自不一样源的document、脚本,同源的意思就是两个URL的域名、协议、端口要彻底相同。
script标签jsonp跨域、nginx反向代理、node.js中间件代理跨域、后端在头部信息设置安全域名、后端在服务器上设置cors
ps:有三个标签容许跨域加载资源: <img src=xxx> <link href = xxx> <script src=xxx>
三个标签场景:
<img> 用于打点统计,统计网站多是其余域
<link> <script> 可使用CDNCDN的也是其余域
<script>能够用于JSONP

⚠️跨域注意事项

全部的跨域请求都必须通过信息提供方容许
若是未经容许便可获取,那么浏览器同源策略出现漏洞

XMLJSON的区别?

数据体积方面
JSON相对于XML来说,数据的体积小,传递的速度更快些。

数据交互方面
JSONJavaScript的交互更加方便,更容易解析处理,更好的数据交互

数据描述方面
JSON对数据的描述性比XML较差

传输速度方面
JSON的速度要远远快于XML

getpost的区别

一、getpostHTTP中都表明着请求数据,其中get请求相对来讲更简单、快速,效率高些
二、get相对post安全性低
三、get有缓存,post没有
四、get体积小,post能够无限大
五、geturl参数可见,post不可见
六、get只接受ASCII字符的参数数据类型,post没有限制
七、get请求参数会保留历史记录,post中参数不会保留
八、get会被浏览器主动catch,post不会,须要手动设置
九、get在浏览器回退时无害,post会再次提交请求

何时使用post

post通常用于修改服务器上的资源,对所发送的信息没有限制。好比
一、没法使用缓存文件(更新服务器上的文件或数据库)
二、向服务器发送大量数据(POST 没有数据量限制)
三、发送包含未知字符的用户输入时,POSTGET 更稳定也更可靠

TCP创建链接为何须要三次?

  • 为了实现可靠数据传输, TCP 协议的通讯双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程便是通讯双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤
  • 若是只是两次握手, 至多只有链接发起方的起始序列号能被确认, 另外一方选择的序列号则得不到确认

ES6

ES5的继承和ES6的继承有什么区别?

ES5的继承时经过prototype或构造函数机制来实现。ES5的继承实质上是先建立子类的实例对象,而后再将父类的方法添加到thisParent.apply(this))。

ES6的继承机制彻底不一样,实质上是先建立父类的实例对象this(因此必须先调用父类的super()方法),而后再用子类的构造函数修改this

具体的:ES6经过class关键字定义类,里面有构造方法,类之间经过extends关键字实现继承。子类必须在constructor方法中调用super方法,不然新建实例报错。由于子类没有本身的this对象,而是继承了父类的this对象,而后对其进行加工。若是不调用super方法,子类得不到this对象。

ps:super关键字指代父类的实例,即父类的this对象。在子类构造函数中,调用super后,才可以使用this关键字,不然报错

javascript如何实现继承?

一、构造继承
二、原型继承
三、实例继承
四、拷贝继承
原型prototype机制或apply和call方法去实现较简单,建议使用构造函数与原型混合方式

function Parent(){
        this.name = 'wang';
    }
    function Child(){
        this.age = 28;
    }
    Child.prototype = new Parent();//继承了Parent,经过原型

    var demo = new Child();
    alert(demo.age);
    alert(demo.name);//获得被继承的属性
  }
复制代码

谈谈你对ES6的理解

新增模板字符串(为JavaScript提供了简单的字符串插值功能)
箭头函数
for-of(用来遍历数据—例如数组中的值。)
arguments对象可被不定参数和默认参数完美代替。
ES6promise对象归入规范,提供了原生的Promise对象。
增长了let和const命令,用来声明变量。
增长了块级做用域。
let命令实际上就增长了块级做用域。
还有就是引入module模块的概念

谈一谈箭头函数与普通函数的区别?

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
  • 不能够看成构造函数,也就是说,不可使用new命令,不然会抛出一个错误
  • 不可使用arguments对象,该对象在函数体内不存在。若是要用,能够用Rest参数代替
  • 不可使用yield命令,所以箭头函数不能用做Generator函数

forEach、for in、for of三者区别

forEach更多的用来遍历数
for in通常经常使用来遍历对象或json
for of数组对象均可以遍历,遍历对象须要经过和Object.keys()
for in循环出的是key,for of循环出的是value

Set、Map的区别

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

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

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

promise对象的用法,手写一个promise

promise是一个构造函数,下面是一个简单实例

var promise = new Promise((resolve,reject) => {
    if (操做成功) {
        resolve(value)
    } else {
        reject(error)
    }
})
promise.then(function (value) {
    // success
},function (value) {
    // failure
})
复制代码

请描述一下Promise的使用场景,'Promise'它所解决的问题以及如今对于异步操做的解决方案。

Promise的使用场景:ajax请求,回调函数,复杂操做判断。
PromiseES6为了解决异步编程所诞生的。
异步操做解决方案:Promise、Generator、定时器(不知道算不算)、还有ES7async

ECMAScript6 怎么写class,为何会出现class这种东西?

这个语法糖可让有OOP基础的人更快上手js,至少是一个官方的实现了 但对熟悉js的人来讲,这个东西没啥大影响;一个Object.creat()搞定继承,比class简洁清晰的多

算法和其余

冒泡排序

每次比较相邻的两个数,若是后一个比前一个小,换位置

var arr = [3, 1, 4, 6, 5, 7, 2];

function bubbleSort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
    for(var j = 0; j < arr.length - i - 1; j++) {
        if(arr[j + 1] < arr[j]) {
            var temp;
            temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}
return arr;
}
console.log(bubbleSort(arr));
复制代码

快速排序

采用二分法,取出中间数,数组每次和中间数比较,小的放到左边,大的放到右边

var arr = [3, 1, 4, 6, 5, 7, 2];
function quickSort(arr) {
    if(arr.length == 0) {
        return [];    // 返回空数组
    }

    var cIndex = Math.floor(arr.length / 2);
    var c = arr.splice(cIndex, 1);
    var l = [];
    var r = [];

    for (var i = 0; i < arr.length; i++) {
        if(arr[i] < c) {
            l.push(arr[i]);
        } else {
            r.push(arr[i]);
        }
    }

    return quickSort(l).concat(c, quickSort(r));
}
console.log(quickSort(arr));
复制代码

懒加载

<img id="img1" src="preview.png" data-realsrc = "abc.png"/>
<script type = "text/javascript">
var img1 = document.getElementById("img1")
img1.src = img1.getAttribute('data-realsrc')
</script>
复制代码

缓存DOM查询

// 未缓存 DOM查询
var i
for (i = 0; i < document.getElementByTagName('p').length; i++) {
    //todo
}

//缓存了DOM查询
var pList = document.getElementByTagName('p')
var i 
for (i= 0; i < pList.length; i++) {
    // todo
}
复制代码

合并DOM插入

var listNode = document.getElementById('list')

//要插入10个li标签
var frag = document.createDocumentFragment();
var x,li
for (x = 0; x < 10; x++) {
    li = document.createElement('li')
    li.innerHTML = "List item" + x
    frag.appendChild(li)
}
listNode.appendChild(frag)
复制代码

事件节流

var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', function () {
    if (timeoutId) {
        clearTimeout(timeoutId)
    }
    timeoutId = setTimeout(function () {
        //触发事件    
    },100)
})
复制代码

尽早操做

window.addEventListener('load', function () {
    //页面的所有资源加载完才会去执行,包括图片,视频
})
document.addEventListener('DOMContentLoaded', function() {
    //DOM 渲染完便可执行,此时图片,视频还可能没有加载完成
})
复制代码

浅拷贝

首先能够经过 Object.assign 来解决这个问题

let a = {
    age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
复制代码

深拷贝

这个问题一般能够经过JSON.parse(JSON.stringify(object)) 来解决

let a = {
    age: 1,
    jobs: {
        first: 'FE'
    }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
复制代码

数组降维

[1, [2], 3].flatMap(v => v)
// -> [1, 2, 3]
复制代码

若是想将一个多维数组完全的降维,能够这样实现

const flattenDeep = (arr) => Array.isArray(arr)
  ? arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , [])
  : [arr]

flattenDeep([1, [[2], [3, [4]], 5]])
复制代码

预加载

在开发中,可能会遇到这样的状况。有些资源不须要立刻用到,可是但愿尽早获取,这时候就可使用预加载 预加载实际上是声明式的 fetch,强制浏览器请求资源,而且不会阻塞 onload 事件,可使用如下代码开启预加载

<link rel="preload" href="http://example.com">
复制代码

预加载能够必定程度上下降首屏的加载时间,由于能够将一些不影响首屏但重要的文件延后加载,惟一缺点就是兼容性很差

预渲染

能够经过预渲染将下载的文件预先在后台渲染,可使用如下代码开启预渲染

<link rel="prerender" href="http://poetries.com">
复制代码

性能优化

JavaScript性能优化

一、尽量把 <script> 标签放在 body 以后,避免 JS 的执行卡住 DOM 的渲染,最大程度保证页面尽快地展现出来
二、尽量合并 JS 代码:提取公共方法,进行面向对象设计等……
三、CSS 能作的事情,尽可能不用 JS 来作,毕竟 JS 的解析执行比较粗暴,而 CSS 效率更高。
四、尽量逐条操做 DOM,并预约好 CSs 样式,从而减小 reflow 或者 repaint 的次数。
五、尽量少地建立 DOM,而是在 HTML 和 CSS 中使用 display: none 来隐藏,按需显示。
六、压缩文件大小,减小资源下载负担。
复制代码

JavaScript几条基本规范

一、不要在同一行声明多个变量
二、请使用===/!==来比较true/false或者数值
三、使用对象字面量替代new Array这种形式
四、不要使用全局变量
五、Switch语句必须带有default分支
六、函数不该该有时候有返回值,有时候没有返回值
七、For循环必须使用大括号
八、IF语句必须使用大括号
九、for-in循环中的变量 应该使用var关键字明确限定做用域,从而避免做用域污染
复制代码