- 本文主要针对前端知识点做了汇总,希望帮助一些正在找工作的同学
- 本人github网址
一、HTML和CSS
1.css3新属性
- transition:过渡 transition:width 2s
- transform: 旋转、缩放、移动或者倾斜
- animation:关键帧动画
- shadow:阴影 box-shadow
- border-radius: 圆角
2.rem em
为了方便计算,时常将在元素中设置font-size值为62.5%
var win_w=document.body.offsetWidth;
//定义变量
var fontSize;
if(win_w>640){
fontSize=24;
}else{
//如果浏览器窗口宽度(这里的实质就是body宽度)值小于320,取320
win_w=Math.max(320,win_w);
fontSize=win_w/320*12
}
//设置根元素的大小
document.getElementsByTagName('html')[0].style.fontSize=fontSize+'px';
理想viewport宽度归纳起来无非也就320、360、414

3.浮动
清除浮动的四种方法:
- 额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐)
- 父级添加overflow属性(父元素添加overflow:hidden)(不推荐)
- 使用after伪元素清除浮动(推荐使用)#content:after{content:"";display:block;clear:both;}
- 使用before和after双伪元素清除浮动
- 父级设置高度
4.BFC相关知识
BFC触发方式:
- 根元素,即HTML标签
- 浮动元素:float值为left、right
- overflow值不为 visible,为 auto、scroll、hidden
- display值为 inline-block、table-cell、table-caption、table、inline-table、flex、inline-flex、grid、inline-grid
- 定位元素:position值为 absolute、fixed
bfc的作用:
- 阻止元素被浮动元素覆盖
- 可以包含浮动元素
- 阻止因为浏览器因为四舍五入造成的多列布局换行的情况
- 阻止相邻元素的margin合并
bfc处理:
- 兄弟级的块之间,margin这个属性上下边距,经常会发生重叠的情况,以数值大的为准,而不会相加。
- 下面一个元素加float浮动 ,同时父元素加bfc 或者
- 下面一个元素加inline-block
- 父子级的块之间,子级的上下margin会与父级上下margin重叠,以数值大的为准,而不会相加。
父级加:
- overflow:hidden
- 加padding
- 加border
子级加:
- position:absolute
5.IE弹性盒子和标准盒子模型
主要区别在于: content 的包含问题,IE盒子模型content包含padding border ,此时的宽度就是content的宽度 标准盒子模型:content不包含其他任何部分。
box-sizing:
- border-box 中,整个 div 的宽、高,包括margin、padding、border。 注解:浏览器f12获取的width为border,padding,content(js获取的宽度),但是在页面现实中占据的宽度为margin、padding、border,content(即需要加上margin)。改变border,padding时,content发生变化,但宽高不发生变化(除非到比较大的值时)。(IE盒子模型ie6)
- content-box 中,整个 div 的宽、高,则不包括上面元素。注解:css设置的宽就是content。而通过js获取获取的宽度或者调试工具看到的宽度为content+border+padding,因此真实的宽度随着前面三个的变化而增加(标准盒子模型)
6.css层级问题

7.经典布局
二、javascript相关知识
1.javascript基本数据类型
6种原始数据类型:
- Boolean: 布尔表示一个逻辑实体,可以有两个值:true 和 false
- Number: 用于表示数字类型
- String: 用于表示文本数据
- Null: Null 类型只有一个值: null,特指对象的值未设置
- Undefined: 一个没有被赋值的变量会有个默认值 undefined
- Symbol: 符号(Symbols)是ECMAScript第6版新定义的。符号类型是唯一的并且是不可修改的
引用类型:Object(Array 、Reg、 function等等)
2.js类型的判断:
- typeof
类型 | typeof返回 |
---|---|
Undefined | undefined |
Null | object |
Boolean | boolean |
Number | number |
String | string |
Object | object |
Function | function |
-
instanceof: 用来判断A 是否是 B的实例,表达式为 A instanceof B,返回一个Boolean类型的值 instanceof 检测的是原型,只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型
-
Object.prototype.toString
console.log(Object.prototype.toString.call(123)); //[object Number]
console.log(Object.prototype.toString.call('123')); //[object String]
console.log(Object.prototype.toString.call(undefined)); //[object Undefined]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call({})); //[object Object]
console.log(Object.prototype.toString.call([])); //[object Array]
console.log(Object.prototype.toString.call(function(){})); //[object Function]
console.log(Object.prototype.toString.call(null)); //[[object Null]]
3.call、applay、bind方法实现
- 这三种方法主要是为了改变this的指向,call和applay的区别主要在于,传递的参数不同,call的参数主要通过逗号隔开传递,而applay的通过第二个参数才用数组形式将,所有参数存于数组中一起传递。
- call/applay跟bind的区别在于,前两者调用后,函数能直接执行,而bind调用后返回的是这个函数,需要再手动调用函数才能执行。
// call apply的实现https://blog.csdn.net/qq_40479190/article/details/78324270
Function.prototype.call = function(context){
var context = context || window;
context.fn = this;
var args = [...arguments].slice(1);
var result = context.fn(...args);
delete context.fn;
return result;
}
Function.prototype.apply = function(context){
var context = context || window;
context.fn= this;
var result;
if(arguments[1]){
result = context.fn(...arguments[1]);
}else{
result = context.fn();
}
delete context.fn;
return result;
}
Function.prototype.bind = function (context) {
var that = this;
return function(){
return that.apply(context);
}
}
Function.prototype.bind2 = function(context){
var that = this;
var args = Array.prototype.slice.call(arguments,1);//arguments不是真实的数组,所以不能直接调用slice方法,需要用这个方法
return function(){
var argslist = [...arguments];//第二参数是调用bind函数后返回的函数再调用的时候传入的参数
return that.apply(context,[...args,...argslist]);//之所以也要用return是因为原函数可能有返回值
}
}
4.async函数的实现
- async底层主要采用generator来实现,举个简单例子:
var fs = require('fs');
var readFile = function(fileName){
return new Promise((resolve, reject) => {
fs.readFile(fileName,(error, data) => {
if(error){
reject(error);
}else{
resolve(data);
}
});
});
}
var gen = function* (){
var f1 = yield readFile('/etc/sdfsdf');
var f2 = yield readFile('/etc/sdfsdf');
console.log(f1.toString());
}
var genFn = gen(); // 调用方法
genFn.next(); // {value:'sdfdf',done:true}
// 改写成async函数
var asyncReadfile = async function(){
var f1 = await readFile('/etc/sdfsdf');
var f2 = await readFile('/etc/sdfsdf');
}
asyncReadfile();
- async 函数的优点相对于Generator:
- 内置执行器:generator函数执行必须靠执行器,而async函数自带执行器,所以async和普通函数的执行一模一样
- 更好的语义。async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
- 更广的适用性co函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
- async函数的实现,就是将Generator函数和自动执行器包含在一个函数里面:
async function fn(args){
// ...
}
//等同于
function fn(args){
return spawn(function* (){});
}// spawn为自动执行器
function spawn(genF){
// genF为generator函数
return new Promise((resolve, reject) => {
var gen = genF();// generator函数调用
function step(nextF){
// 封装的执行器函数
try{
var next = nextF();
}catch(e){
return reject(e);
}
// 没有返回错误判断generator的返回结果
if(next.done){
// 如果next.done为true表示generator已经执行完了
return resolve(next.value);
}
// 如果没有执行完,则执行Promise.resolve(next.value)生成一个Promise对象然后继续递归调用
Promise.resolve(next.value).then((v) => {
step(() => {
return gen.next(v);
});
}, (err) => {
step(() => gen.throw(e));
});
}
step(() => gen.next(undefined));
});
}
5.函数式编程中compose函数的实现
- 用于将多个函数合并成并执行
function compose(){
var args = arguments;
var start = args.length -1 ;
return function(){
var i = start;
var result = args[start].apply(this,arguments);
while (i--) result = args[i].call(this, result);
return result;
}
}
6.函数节流和防抖
-
函数防抖:
在事件被触发n秒后再次执行回调,如果在n秒内又被触发,则重新计时(函数防抖就是法师发技能的时候要吟唱,技能读条没完再按技能就会重新读条)
-
应用场景:
- search搜索联想,用户不断输入值时,用防抖节约请求资源
- window出发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其触发一次
-
// 防抖debounce
// 第一版
function debounce(func, wait){
var timeout;
return function(){
clearTimeout(timeout);
timeout = setTimeout(func, wait);
}
}
// this
function debounce(func,wait){
let timeoutNumber;
return function() {
let that = this;
clearTimeout(timeoutNumber);
timeoutNumber = setTimeout(function(){
func.applay(that);
},wait);
}
}
// 可能在执行的过程带事件,所以需要传递参数
function debounce(func,wait){
var timeoutNumber;
return function() {
let args = arguments;
clearTimeout(timeoutNumber);
timeoutNumber = setTimeout(() => {
func.apply(this,args);
},await);
}
}
// 立即执行,一开始就执行下来
function debounce(func,wait, immediate) {
var timeout;
var result;
return function(){
var that = this;
var args = arguments;
if (timeout) {
clearTimeout(timeout);
}
if(immediate){
var callNow = timeout; // 有表示已经执行过了
timeout = setTimeout(() => {
timeout = null;
}, wait);
if(callNow){
result = func.applay(that,args);
}
} else {
timeout = setTimeout(() => {
result = func.applay(that,args);
}, wait);
}
return result; // 函数的返回值
}
}
// 取消
function debounce(func,wait, immediate) {
var timeout;
var result;
var debound = function(){
var that = this;
var args = arguments;
if (timeout) {
clearTimeout(timeout);
}
if(immediate){
var callNow = timeout; // 有表示已经执行过了
timeout = setTimeout(() => {
timeout = null;
}, wait);
if(callNow){
result = func.applay(that,args);
}
} else {
timeout = setTimeout(() => {
result = func.applay(that,args);
}, wait);
}
return result; // 函数的返回值
}
debound.cancel = function(){
clearTimeout(timeout);
timeout = null;
};
return debound;
}
-
函数节流:
规定在一个单位时间内,只能触发一次函数,如果这个单位时间内触发多次函数,只有一次生效(个人理解:函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。)
- 应用场景
- 鼠标不断触发点击触发,mousedown单位时间内只触发一次
- 监听滚动事件,比如是否滑动到底部自动加载更多,用throttle来判断
- 应用场景
// 利用时间戳来实现节流函数
function throttle(func,wait){
var previous=0;
var that = 0;
return function(){
var now=+ new Date();
args = arguments;
if(now - previous > wait){
func.apply(that, args);
previous = now;
}
}
}
const throttle2 = function(fn, delay) {
let preTime = Date.now()
return function() {
const context = this
let args = arguments
let doTime = Date.now()
if (doTime - preTime >= delay) {
fn.apply(context, args)
preTime = Date.now()
}
}
}
// 利用定时器来实现
function throttle(func,wait){
var timeout;
return function(){
var args = arguments;
if(!timeout){
timeout = setTimeout(() => {
time = null;
func.apply(this, args);
},wait);
}
}
}
const throttle = function(fn,delay) {
let timer = null
return function() {
const context = this
let args = arguments
if(!timer) {
timer = setTimeout(() => {
fn.apply(context,args)
clearTimeout(timer)
},delay)
}
}
}
//第三版
function throttle(func,wait){
var timeout,context,args,result;
var previous = 0;
var later = function(){
previous = + new Date();
timeout = null;
func.apply(context, wait);
}
return function(){
var now = + new Date();
var remaintime = wait - (now - previous);
context = this;
args = arguments;
if(remaintime <= 0 || remaintime > wait){
if (timeout){
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context,args);
} else if(!timeout){
timeout = setTimeout(later, remaintime);
}
}
}
function throttle(method, mustRunDelay) {
let timer,
args = arguments,
start;
return function loop() {
let self = this;
let now = Date.now();
if(!start){
start = now;
}
if(timer){
clearTimeout(timer);
}
if(now - start >= mustRunDelay){
method.apply(self, args);
start = now;
}else {
timer = setTimeout(function () {
loop.apply(self, args);
}, 50);
}
}
}
function throttle(fn,wait){
let timeout,
args,
start;
return function (){
args = arguments;
let that = this;
let now=Date.now();
if(!start){
start = now;
}
if(timeout){
clearTimeout(timeout);
}
}
}
7.深拷贝和浅拷贝
- 如果一种引用类型的变量,通过赋值的形式传递给新的变量,新的变量中的某个属性发生变化,原来那个变量对应值也会变化,因为引用类型的变量,都是指向某个指针。这样就出来拷贝的问题。
浅拷贝
- Object.assign
let a = {
age: 1
}
let b = Object.assign({}, a);
a.age = 2;
console.log(b.age)// 1
- ...扩展运算符
let a = {
age: 1
}
b = {...a};
a.age = 2;
console.log(b.age);//1
- Array.prototype.slice(0)可以拷贝数组
深拷贝
- 当对象里面嵌套对象或者数组时候就需要用深拷贝
- JSON.parse(JSON.stringify(object))实现
- 会忽略undefined
- 不会序列化函数
- 不能解决循环引用的对象
- 递归的实现:
// 数组的浅拷贝: slice concat方法可以实现
function clone(arr){
var new_arr = arr.concat();
// var new_arr = arr.slice();
return new_arr;
}// 此方法只能拷贝没有嵌套对象或者数组的
// JSON.stringify();但是不能拷贝函数
// 浅拷贝的实现
function shalloeCopy(obj){
if(typeof obj !== 'object'){
return;
}
var newObj = obj instanceof Array ? [] : {};
for (var i in obj){
console.log(i);
if(obj.hasOwnProperty(i)){
newObj[i] = obj[i]
}
}
return newObj;
}
shalloeCopy({sdf:2,sdfdfd:1});
// 深拷贝
function deepClone(obj){
if(typeof obj !== 'object'){
return;
}
var newObj = obj instanceof Array ? [] : {};
for(var key in obj){
if(obj.hasOwnProperty(key)){
// 防止原型链上的属性
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return newObj;
}
8.js事件循环
- 时间队列执行顺序script(主程序代码)—>process.nextTick—>Promises...——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering
- nodejs中setTimeout() setInterval() setImmeditate() process.nextTick()两者都代表主线程完成后立即执行,其执行结果是不确定的,可能是setTimeout回调函数执行结果在前,也可能是setImmediate回调函数执行结果在前,但setTimeout回调函数执行结果在前的概率更大些,这是因为他们采用的观察者不同,setTimeout采用的是类似IO观察者,setImmediate采用的是check观察者,而process.nextTick()采用的是idle观察者。
- 三种观察者的优先级顺序是:idle观察者>>io观察者>check观察者
- process.nextTick()在异步队列之前,主线程之后
- process.nextTick(),效率最高,消费资源小,但会阻塞CPU的后续调用; setTimeout(),精确度不高,可能有延迟执行的情况发生,且因为动用了红黑树,所以消耗资源大;setImmediate(),消耗的资源小,也不会造成阻塞,但效率也是最低的。
9.extend的实现
function extend(){
var args = arguments;
var target = args[0];
var objs;
var newObj;
for(var i = 0; i<args.length;i++){
objs = args[i];
if(objs != null){
for(var name in objs){
target[name] = objs[name];
}
}
}
}
10.flatten扁平化和函数柯里化
- 偏函数和柯里化的区别:
- 偏函数,固定了你函数的某一个或几个参数,返回一个新的函数,接收剩下的参数, 参数个数可能是1个,也可能是2个,甚至更多。
- 柯理化是把一个有n个参数的函数变成n个只有1个参数的函数
//偏函数 n 变成n-1元,只能一个个减少,m跟柯里化的区别
function partial(fn){
var args = [].slice.call(arguments, 1);
return function(){
var newArgs = args.concat([].slice.call(arguments));
return fn.applay(this,newArgs);
}
}
// 第二版
function partial(fn){
var args = [].slice.call(arguments,1);
return function(){
var position=0,len = args.length;
for(var i=0;i<len;i++){
args[i] = args[i] === _ ? arguments[position++] : args[i]
}
while(position < arguments.length) args.push(arguments[position++]);
return fn.apply(this, args);
}
}
// flatten扁平化
function flatten(){
let arr = arguments[0];
var result = [];
for(var i=0;i < arr.length;i++){
if(Array.isArray(arr[i])){
result = result.concat(flatten(arr[i]));
}else{
result.push(arr[i]);
}
}
return result;
}
// toString
function flatten(arr){
return arr.toString().split(',').map(item => +item);//+item是将字符串转化成数字,但是数组中有字符串的不行
}
// reduce实现
function flatten(arr){
return arr.reduce(function(prev,next){
return prev.concat(Array.isArray(next)?flatten(next):next)
}, []);
}
- 函数柯里化
// 第一版
function curry(fn){
var args = [].slice.call(arguments, 1);//最外面的arguments,此处指的的是调用curry函数时候除了第一个函数外剩下的参数
return function(){
var newArgs = args.concat([].slice.call(arguments));//此处的arguments和前面的arguments不是一个,此处表示调用curry返回的函数再调用的时候又传的参数
return fn.apply(this,newArgs);
}
}
function add(a, b) {
return a + b;
}
var addCurry = curry(add, 1, 2);
addCurry() // 3
//或者
var addCurry = curry(add, 1);
addCurry(2) // 3
//或者
var addCurry = curry(add);
addCurry(1, 2) // 3
// 第二版
function sub_curry(fn) {
var args = [].slice.call(arguments, 1);
return function() {
return fn.apply(this, args.concat([].slice.call(arguments)));
};
}
function curry(fn, length){
length = length || fn.length; // 判断fn有多少形参
var slice = Array.prototype.slice; // arguments是非数组要调用该方法需要应用
return function(){
if(arguments.length < length){ // 表示调用柯里化后的函数参数没有传完
var combined = [fn].concat(slice.call(arguments)); // 柯里化后再调函数还需要的参数,与fn合成一个数组
return curry(sub_curry.apply(this, combined),length - arguments.length); // 总共需要的形参减去用过的形参
}else{
// 所调用的形参足够直接调用
return fn.apply(this,arguments);
}
}
}
// 通用性 适用性 前几个大量计算
// 1.参数复用性,
// http() https() get() getJson()
// 2。提前返回
var addEvent = function(el, type, fn ,capture){
if(window.addEventListener){
el.addEventListener();
}else{
el.atachEvent();
}
}
addEvent(p,click,callnack,true);
addEvent(p,click,callnack,true);
// 这样前面的代码调用了很多次
var addEvent = function(){
if(window.addEventListener){
return function(el, type, fn ,capture){
el.addEventListener();
}
}else {
return function(el, type, fn ,capture){
el.atachEvent();
}
}
}
var elBind = addEvent();
// 3.延迟执行 ,不定参数作为最后一次调用(空间换时间)
var allScore = 0;
var addScore = function(score){
// 对分数的判断 多少个三分 计算里程碑
allScore += score;
};
addScore(1);
addScore(2);
addScore(3); // 但是这样每次就加了
// 虚拟缓存,代理缓存
var curryScore = function(fn){
var _allScore = [];
return function(){
if(arguments.length === 0){
// 表示统计了
return fn.apply(null, _allScore);
}else{
_allScore = _allScore.concat([].slice.call(arguments));
}
}
};
var curryAddScore = curryScore(function(){
for(var i=0;i<arguments.length;i++){
allScore+=arguments[i]
}
});
curryAddScore(1);//0
curryAddScore(2);//0
curryAddScore();//3 不传参数的时候开始执行
11.each的原生实现:
function each(obj, callback){
var length,i=0;
if(isArrayLik(obj)){
length = obj.length;
for(;i<obj.length;i++){
let isOut = callback.call(obj[i],i,obj[i]);
if(isOut === false){
break;// 跳出循环
}
}
}else{
for (i in obj){
let isOut = callback.call(obj[i],i,obj[i]);
if(isOut === false){
break;
}
}
}
return obj;
}
12.promise的实现原理
// 第一种实现
function MyPromise(fn){
let self = this;
self.value = null;
self.error = null;
self.onFulfilled = null;
self.onRejected = null;
function resolve(value){
self.value = value;
self.onFulfilled(self.value);
}
function reject(err){
self.error = err;
self.onRejected(self.error);
}
fn(resolve,reject);
}
MyPromise.prototype.then = function(onFulfilled,onRejected){
this.onFulfilled = onFulfilled;
this.onRejected = onRejected;
}
module.exports = MyPromise;
- 增加状态参数的改进实现:
// 改进方案 const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; function Promise(fn){ let self = this; self.value = null; self.error = null; self.status = PENDING; self.onFulfilled = null; self.onRejected = null; function resolve(value){ if(self.status === PENDING){ setTimeout(function(){ self.status = FULFILLED; self.value = value; self.onFulfilled(self.value); }); } } function reject(error){ if(self.status === PENDING){ setTimeout(function(){ self.status = REJECTED; self.error = error; self.onRejected(self.error); }); } } fn(resolve,reject); } MyPromise.prototype.then = function(onFulfilled, onRejected) { if (this.status === PENDING) { this.onFulfilled = onFulfilled; this.onRejected = onRejected; } else if (this.status === FULFILLED) { //如果状态是fulfilled,直接执行成功回调,并将成功值传入 onFulfilled(this.value) } else { //如果状态是rejected,直接执行失败回调,并将失败原因传入 onRejected(this.error) } return this; }
- promise 链式
const PENDING= 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; function Promise(fn){ let self = this; self.value = null; self.error = null; self.status = PENDING; self.onFulfilledCallbacks = []; self.onRejectedCallbacks = []; function resolve(value){ if(self.status === PENDING){ setTimeout(() => { self.status = FULFILLED; self.value = value; self.onFulfilledCallbacks.forEach((callback) => { callback(self.value) }); },0); } } function reject(error){ if(self.status === PENDING){ setTimeout(()=>{ self.status = REJECTED; self.error = error; self.onRejectedCallbacks.forEach((callback) => { callback(slef.error); }); },0); } } fn(resolve,reject); } Promise.prototype.then = function(onFulfilled,onRejected){ if(this.status === PENDING){ this.onFulfilledCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); }else if(this.status === FULFILLED){ onFulfilled(this.value); }else { onRejected(this.error); } return this; }
- Promise.all的实现:
function promiseAll(promises){ return new Promise(function(resolve, reject){ if(!(promises instanceof Array)){ throw new TypeError('promises must be an Array'); } var len = promises.length; var resolveCount = 0; var resolvedArray = new Array(len); for(var i=0;i<len;i++){ (function(i){ Promise.resolve(promises[i]) .then((value) => { resolveCount++; resolvedArray[i] = value; if(resolveCount == len){ return resolve(resolvedArray); } },re => { return reject(re); }).catch((re) => { console.log(re); }); })(i) } }); }
13.数组去重
// 数组去重
function unique(arr){
var res = [];
for(var i=0;i<arr.length;i++){
// 注明,此处可以用数组方法includes 和indexOf方法来判断
for(var j=0;j<res.length;j++){
if(arrp[j] == res[j]){
break;
}
if(j === res.length){
res.push(arr[i]);
}
}
}
return res;
}
// 排序后再去重
function unique(arr){
var res = arr.filter(function(item,index,arr1){
return arr.indexOf(item) === index;
});
return res;
}
function unique(arr){
var obj = {};
return arr.filter(function(item,index,arr1){
return obj.hasOwnProperty(typeof item + item) ? false : (obj[item] = true)
});
}
14.websocket的实现
- 一般为了实时获取服务端最新数据常采用轮询的方法,websocket的出现,服务端和客户端能够实时进行通信,而不需要断开连接。聊天室常采用这种技术
- 基本用法:
var ws; var lockReconnect = false; var wsUrl = 'ws:xxx.1.1.1'; function initEventHandle() { ws.onclose = function() { reconnet(); }; ws.onerror = function(){ reconnect(); }; ws.onopen = function() { heartChect.reset().start(); }; ws.onmessage = function () { heartChect.reset().start(); }; } function reconnect(url){ if(lockReconnect){ return; } lockReconnect = true; setTimeout(function(){ createWebSocket(url); }); }