写一下 Javascript 的原始类型

原始类型:number,string,boolean,null,undefined

引用类型:object。里面包含function,Array,Date

Typeof null // 输出object(null 被认为是对象的占位符)

例举3种强制类型转换和2种隐式类型转换?

强制(parseInt, parseFloat, number)
隐式(== ===)

parseInt() 函数可解析一个字符串,并返回一个整数。
parseFloat() 函数可解析一个字符串,并返回一个浮点数。
Number() 函数把对象的值转换为数字。

注意使用parseFloat()时:
1.字符串中只返回第一个数字。
2. 开头和结尾的空格是允许的。
3. 如果字符串的第一个字符不能被转换为数字,那么 parseFloat() 会返回 NaN。

列举鼠标事件。

onClick 鼠标点击事件
onDblClick 双击事件
onMouseOver 鼠标经过
onMouseOut 鼠标移出
onMouseDown 鼠标上的按钮被按下了
onMouseUp 鼠标按下后,松开时激发的事件

表单相关事件
onFocus 获得焦点
onBlur 失焦
onChange 改变文本框内容触发
onSubmit 一个表单被提交时触发的事件

数组方法pop() push() unshift() shift()

push() 数组尾部添加
pop() 尾部删除
unshift() 数组头部添加
shift() 头部删除

数组的截取和合并

concat() 合并数组

var arr = [1,2,3];
var arr1 = [4,5,6];
var arr2 = arr.concat(arr1);
console.log(arr2); // [1,2,3,4,5,6];

slice() 来截取数组。

var arr = [1,2,3];
console.log(arr.slice(0,1);); // [1];
// slice方法接受两个参数,一个从哪里开始,一个是到哪里结束(但是不包括这个结束的元素本身)。如果是负数,就从倒数第几个。

删除指定数组

splice()从数组中添加/删除项目,然后返回被删除的项目

var arr = [1,2,3];
arr.splice(0,1); //从数组下标0开始,删除1个元素
console.log(arr); // [2,3];
// 接受两个参数,第一个是index,数组开始的下标,第二个是len,要删除的长度

数组元素的排序

reverse()颠倒数组中元素的顺序

let a = [1,2,3];
a.reverse();
console.log(a); // [3,2,1]

sort()方法对数组元素进行排序,并返回这个数组。
sort排序常见用法:数组元素为数字的升序、降序:

var array = [10, 1, 3, 4,20,4,25,8];
array.sort(function(a,b){return a-b; //升序
});
console.log(array); // [1,3,4,4,8,10,20,25];
var array = [10, 1, 3, 4,20,4,25,8];
array.sort(function(a,b){return b-a; //降序
});
console.log(array); // [25,20,10,8,4,4,3,1];

数组转字符串

join()方法用于把数组中的所有元素通过指定的分隔符进行分隔放入一个字符串,返回生成的字符串

let a= ['hello','world'];
let str=a.join(); // 'hello,world'
let str2=a.join('+'); // 'hello+world'

关于数组具体实例可参考 js中数组的操作方法

JS中提取字符串的方法

substring(start,end)表示从start到end之间的字符串,包括start位置的字符但是不包括end位置的字符。

var src="images/off_1.png";
alert(src.substring(7,10));//off

字符串实现倒序输出

newStr = str.split('').reverse().join('')

数组去重?

此题看着简单,但要想面试官给你高分还是有难度的。至少也要写出几种方法
js

var array=['12','32','89','12','12','78','12','32'];// 最简单数组去重法//新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,//如果有相同的值则跳过,不相同则push进数组。function unique1(array){var n = []; //一个新的临时数组for(var i = 0; i < array.length; i++){ //遍历当前数组if (n.indexOf(array[i]) == -1)n.push(array[i]);}return n;}arr=unique1(array);// 速度最快, 占空间最多(空间换时间)function unique2(array){var n = {}, r = [], type;for (var i = 0; i < array.length; i++) {type = typeof array[i];if (!n[array[i]]) {n[array[i]] = [type];r.push(array[i]);} else if (n[array[i]].indexOf(type) < 0) {n[array[i]].push(type);r.push(array[i]);}}return r;}//数组下标判断法function unique3(array){var n = [array[0]]; //结果数组for(var i = 1; i < array.length; i++) { //从第二项开始遍历if (array.indexOf(array[i]) == i) n.push(array[i]);}return n;}

es6

es6方法数组去重,第一种方法
let arr = [1,2,3,4,2,1,2,3,4,12,3,1,2,31,1]
let s = [...new Set(arr)];
es6方法数组去重,第二种方法
function dedupe(array) {return Array.from(new Set(array));       //Array.from()能把set结构转换为数组
}

数组的遍历?

一般有3种方法遍历数组:

  1. for循环
  2. for each ( ES5 新增)
  3. for… in

1.1 使用普通for循环遍历数组

var arr = [50, 20, 10, 5, 15, 6];
for(var i = 0; i < arr.length; i++){    //数组长度多长,就遍历多少次。  循环变量作为数组的下标console.log(arr[i]);
}

2.1 使用for …each遍历数组

ES5为每个数组新增了一个方法 array.forEach(function) ,使用这个方法,可以自动帮我们遍历数组中的所有元素

var arr = [50, 20, 10, 5, 15, 6];
//调用数组的forEach方法,传入一个匿名函数
//匿名函数接受两个参数:   参数1--迭代遍历的那个元素  参数2:迭代遍历的那个元素的下标
//可以在匿名函数内部书需要的代码
arr.forEach( function(element, index) {alert(element);
});

3.1 使用 for…in 循环遍历数组

for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性和数组的元素。示例:

var arr = [50, 20, 10, 5, 15, 6];
// 每循环一轮,都会把数组的下标赋值给变量index,然后num就拿到了每个元素的下标。
//注意:这里index是元素的下标,不是与元素
//对数组来说,index从从0开始顺序获取下标
for (var index in arr) {console.log(num);  //循环输出: 0 1 2 3 4 5
}
//这里var 关键字也是可以省略的,但是不建议省略。
for(i in arr){console.log(arr[i]);
}

列举出两个伪数组,如何转化成数组?

伪数组:有函数三要素(下标,元素,长度),没有数组的api

        var weiArr = {0: 22,1: 44,2: 55,3: 77,4: 99,length: 5}
 **方法一:声明一个空数组,遍历伪数组,将伪数组中的元素添加到真数组中**var arr=[];for(var i = 0;i<weiArr.length;i++){arr.push(weiArr[i]);}
**方法二(常用):arr.push.apply(arr,伪数组)**var arr=[];arr.push.apply(arr,weiArr);//不需要修改push的this,只是利用apply的传参特点console.log(arr);
**方法三 arr.slice(0):如果是固定参数0,则会返回数组自身**
//call(weiArr,0)  : 把slice()中的this 从原型对象 修改为 伪数组weiArr=Array.prototype.slice.call(weiArr,0)

IE和标准下有哪些兼容性的写法

var ev = ev || window.event
document.documentElement.clientWidth || document.body.clientWidth
Var target = ev.srcElement||ev.target

事件委托是什么??

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

事件委托利用事件冒泡原理来实现,何为事件冒泡?就是事件从最深节点开始,然后逐步向上传播事件,比如页面有一个节点树,div > ul > li > a, 给最里面a加一个click点击事件,那么这个事件就会一层层往外执行,执行顺序 a > li> ul > div,既然有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div,所以都会触发,这就是事件委托,委托他们的父系级代为执行事件。

如何阻止事件冒泡?

event.stopPropagation();

<script type="text/javascript">$(function() {$("#hr_three").click(function(event) {event.stopPropagation();});});
<script>

可参考:JS如何阻止事件冒泡

如何阻止默认事件?

1.return false; 2.event.preventDefault();

//return false; 事件处理过程中,阻止了事件冒泡,也阻止了默认行为
<script type="text/javascript">
$(function() {$("#hr_three").click(function(event) {return false;});
});
<script>
//event.preventDefault(); 事件处理过程中,不阻击事件冒泡,但阻击默认行为
<script type="text/javascript">
$(function() {$("#hr_three").click(function(event) {event.preventDefault();});
});
<script>

Javascript的事件流模型都有什么?

先解释下事件流是什么:当你在页面触发一个点击事件后,页面上不仅仅有一个元素响应该事件而是多个元素响应同一个事件,因为元素是在容器中的。事件发生的顺序就是事件流,不同的浏览器对事件流的处理不同。

“事件冒泡”:事件开始由最具体的元素接收,然后逐级向上传播
“事件捕捉”:事件由最不具体的节点先接收,然后逐级向下,一直到最具体的
“DOM事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡

添加 删除 替换 插入到某个接点的方法?

(1)创建节点
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点

(2)添加、移除、替换、插入
appendChild() //最后1个子节点之后添加一个新子节点
removeChild() //删除子节点
replaceChild() //替换子节点
insertBefore() //在已有的子节点前插入一个新节点

(3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值
getElementById() //通过元素Id,唯一性

遍历A节点的父节点下的所有子节点

这题考查原生的js操作dom,属于非常简单的基础题,但长时间使用mvvm框架,可能会忘记

<script>var b=document.getElementById("a").parentNode.children;console.log(b)
</script>

document load 和document ready的区别?

document.onload 是在结构和样式,外部js以及图片加载完才执行js。
document.ready是dom树创建完成就执行的方法,原生种没有这个方法,jquery中有 $().ready(function)

什么是回调函数?

  把a函数作为b函数的参数,传递给b函数,则这个a函数就叫做b的回调函数
var a = function(){}
var b = function(callback){}
b(a); //把a函数作为参数传递给b函数,则a就是b的回调函数

看下面代码,给出输出结果。

for(var i = 0; i < 5; i++){  //建议使用let 可正常输出i的值setTimeout(function(){console.log(i);   },0);
};
//答案:5 5 5 5 5
//原因:由于 JavaScript 中的事件循环机制,setTimeout() 并不会立即执行,而是会在当前执行栈的任务全部执行完毕后才会执行,因此打印的结果会是 5 个 5。
//如果想要按照预期输出 0、1、2、3、4,可以使用 let 关键字来定义变量 i,因为 let 会创建块级作用域,使得每个循环的 i 变量都有自己的作用域。

回答以下代码,alert的值分别是多少?

<script>var a = 100;  function test(){  alert(a);  a = 10;  //去掉了var 就变成定义了全局变量了alert(a);
}
test();
alert(a);
</script>
//正确答案是: 100, 10, 10

具体可查询:js变量的作用域详解

以下代码执行结果?

var uname = 'jack'
function change() {alert(uname) // ?var uname = 'lily'alert(uname)  //?
}
change()
//分别alert出 undefined,lily,(变量声明提前问题)

javaScript的2种变量范围有什么不同?

全局变量:当前页面内有效
局部变量:函数方法内有效

变量提升和函数提升的区别?

变量提升和函数提升是JavaScript中的两个重要概念,它们都是在代码执行前将声明的变量或函数提前到当前作用域的顶部

但是,它们之间有一个关键的区别:即函数声明会被整个提升到当前作用域的顶部,而变量声明只会被提升。

  1. 变量提升:在JavaScript中,所有声明的变量都会在代码执行前被提升到当前作用域的顶部,但是只有声明的变量会被提升,而变量的赋值不会被提升。例如:
console.log(a); // undefined,因为只是声明的变量被提升,赋值操作并没有被提升
var a = 1;
  1. 函数提升:与变量提升不同,函数的声明会被整个提升到当前作用域的顶部,并且函数的定义和函数名都会被提升。例如:
foo(); // "hello"
function foo() {console.log("hello");
}

如何获取javascript三个数中的最大值和最小值??

Math.max(a, b ,c) //最大值
Math.min(a, b ,c) //最小值

javascript是面向对象的,怎么体现javascript的继承关系???

使用prototype原型来实现。

一个简单的例子:

var A =function(){}A.prototype = {v : 5,tmp : 76,echo : function(){console.log(this.tmp);},}  //v、tmp、echo 都是属于A的原型,var b = new A();b.echo();  //但是此处会发现b也可以调用A的所有原型,而b并没有声明任何echo()方法

程序中捕获异常的方法?

try{}catch(e){}finally{}

null和undefined的区别

在JavaScript中,null 和 undefined 几乎相等

console.log(null==undefined);    //true  因为两者都默认转换成了false
console.log(null===undefined);    //false   "==="表示绝对相等,null和undefined类型是不一样的,所以输出“false”
console.log(typeof undefined);    //"undefined"
console.log(typeof null);       //"object"

可以发现:null和undefined 两者相等,但是当两者做全等比较时,两者又不等。
原因:类型不一样
null: Null类型,代表“空值”,代表一个空对象指针,使用typeof运算得到 “object”,所以你可以认为它是一个特殊的对象值。
undefined: Undefined类型,当一个声明了一个变量未初始化时,得到的就是undefined。

那到底什么时候是null,什么时候是undefined呢???

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

(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。

undefined表示缺少值,即此处应该有值,但没有定义。典型用法是:

(1)定义了形参,没有传实参,显示undefined

function fn(num1, num2, num3) {return num3;
}
fn(10, 20); //undefined

(2)变量提升

console.log(num); //undefined
var num = 10;

(3)对象属性名不存在时,显示undefined

var obj = {name: '李四',age: 24}
console.log(obj.weight); //undefined

(4)定义变量,但未赋值

var a;
console.log(a); //undefined

(5)函数没有写返回值,即没有写return,拿到的是undefined

function fn() {var num = 1;console.log(num);//1
}
console.log(fn());//undefined

(6)写了return,但没有赋值,拿到的是undefined

function fn() {return;
}
console.log(fn()); //undefined

写一下 js的比较运算符,逻辑运算符

比较运算符:

>    大于
<    小于
>=   大于或等于
<=   小于或等于
==   等于
!=   不相等
===  值相等并且类型相等
!=== 值不相等或类型不相等

逻辑运算符:


比较运算符和逻辑运算符用于测试true和false

== 和 === 的区别

=是赋值的意思;==是判断的意思;===也是判断,但是它比==判断更严谨一点,他的判断必须什么都相等,比如类型,值。

 console.log(1==='1') // false, 因为前面的1是数字类型,后面的1是字符串类型console.log([1,23] == [1,23]) // false,因为都是引用类型,指向的地址值不一样console.log("" == []) //true,空字符串和数组都被强制转换成了数值0

JS中的let和var的区别

  1. 作用域:使用 var 声明的变量属于全局变量或者函数作用域,即在声明它的函数内部有效,而使用 let 声明的变量属于块级作用域,即在声明它的语句块内部有效(例如,if、for、while、try 等语句块)。这意味着使用 var 声明的变量在函数外部也可以访问,而使用 let 声明的变量只能在语句块内部访问。

  2. 变量提升: 使用 var 声明的变量会被提升函数作用域的顶部,也就是说,即使在变量声明之前使用了变量,也不会报错,只是返回 undefined。而使用 let 声明的变量不会被提升,如果在声明之前使用变量,会抛出 ReferenceError

  3. 重复声明:在同一个作用域中,使用 var 声明同名的变量不会报错,而是会覆盖原来的变量。而使用 let 声明同名的变量会报错

可参考: 前端面试题:JS中的let和var的区别

记录一个实际案例:

openPopup(){try {var cardId = this.cardId } catch(error){this.$toast('该游戏暂不支持红包充值,请联系客服处理')return}
}

如果这里用let,那么这个变量必须写在try外面,才能让后面的代码调用。
var 是 函数作用域,这样写就可以少写一行。

let 与 const 异同

const 与 let 很类似,都具有上面提到的 let 的特性,唯一区别就在于 const 声明的是一个只读变量声明之后不允许改变其值。因此,const 一旦声明必须初始化,否则会报错。

let a;
const b = "constant"a = "variable"
b = 'change' // TypeError: Assignment to constant variable

this在Es5 和 Es6的区别

this在ES5中默认指向window对象,在ES6的vue中默认指向Vue实例,基本没什么区别,都是指向最顶层的对象

this在函数中的时候,指向的是这个函数的作用域,当在函数中出现回调等情况的时候,回调函数中的this作用域会变成指向该回调函数,因此一般会在函数中使用 var that = this来保存this。

例如ES5中 this 的指向

var x = function(){this.a = 'a';this.b = 'b';this.c = {a:'1',b:function(){return this.a}}  };console.log(new factory().c.b()); //结果:1//调用函数返回值为1,this 的指向是该函数被调用的对象,也就是说函数被调用的时候,this 指向的是对象调用的这个函数。

而ES6新引入了箭头函数,在箭头函数中,this指向上下文对象,因此可以不用再写 var that=this了

可参考:浅析this在ES5 和 ES6中使用的区别

如果 new 一个箭头函数的会怎么样?

箭头函数是ES6中提出来的,它没有 prototype,也没有自己的 this 指向,更不可以使用 arguments 参数,所以不能 new 一个箭头函数,控制台会报错。

new操作符的实现步骤如下:

  1. 首先创建一个空对象(创建一个新的存储空间)
  2. 将构造函数的作用域赋给新对象(也就是将对象的_proto_属性指向构造函数的 prototype 属性)
  3. 指向构造函数的代码,让构造函数中的 this 指向新对象(为新对象添加属性和方法)
  4. 返回新对象(所以构造函数不需要return)

所以,上面的第2、3步,箭头函数都是没有办法执行的。

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

箭头函数是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几点差异:

  • 箭头函数没有 this,它会从自己的作用域链的上一层继承 this(因此无法使用 apply / call / bind 进行绑定 this 值);
  • 不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 …rest 参数代替。
  • 没有 prototype 属性,即指向 undefined;
  • 不可以使用 new 命令,因为:
    1)没有自己的 this,无法调用 call、apply,将 this 指向实例对象。
    2)没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 proto

什么是闭包?手写一个闭包函数?闭包作用以及优缺点?

可参考:闭包的作用和优缺点。闭包作用这里讲的很详细!!!!!!

  • 闭包让你可以在一个内层函数中访问到其外层函数的作用域 函数嵌套函数。
  • 延长变量的生命周期 在内存中维持一个变量。

简单理解就是,一个作用域可以访问另一个函数内部的局部变量

function fn() {var num = 10;function fun() {console.log(num);}return fun;
}
var f = fn();
f();

作用:

1.读取函数内部的变量;
2.这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。详细描述就是:
涉及到变量的生命周期问题了,函数内部定义的变量属于局部变量,局部变量的生命周期是:当它所在的函数被调用的时候,就是开始,当调用执行一旦结束,局部变量就会被释放,当我们需要函数内部变量时,他已经被释放了,读取不到了,这个时候怎么解决?我们就要想办法延长他的生命周期
闭包的目的也可以说就是这个,延长局部变量的生命周期,当函数执行完毕以后,局部变量不可以被内存释放,然后让外部可以访问到这个变量

闭包形成的条件:

 - 函数的嵌套- 内部函数引用外部函数的局部变量,延长外部函数的变量生命周期

优点:

优点:
1:变量长期驻扎在内存中
2.延长变量的生命周期

闭包的坏处:

在函数外部可以访问函数内部的局部变量,容易造成内存泄露,因为闭包中的局部变量永远不会被回收。
解决这个问题的办法就是在不使用这些变量时,及时把不需要的局部变量全部删除

闭包应用场景:

闭包的两个场景,闭包的两大作用:保存/保护。
在开发中, 其实我们随处可见闭包的身影, 大部分前端JavaScript 代码都是“事件驱动”的,即一个事件绑定的回调方法;
发送ajax请求成功|失败的回调;
setTimeout的延时回调;
或者一个函数内部返回另一个匿名函数,这些都是闭包的应用。

什么叫内存泄漏?除闭包之外,还知道哪些会导致内存泄漏?

在javaScript中,内存泄漏指的是程序分配的内存空间因为某些原因而无法被垃圾回收机制正确的释放。这通常是由于代码中持续引用着不需要的对象或者变量,或者存在未及时清除的定时器或者事件监听器等情况所致。如果内存泄漏问题长期存在,会导致浏览器卡顿或者崩溃,影响用户体验。

其他内存泄漏:

  1. 没有清理定时器或事件监听器:如果定时器或事件监听器没有被正确清理,则会一直占用内存,导致内存泄漏。

  2. 互相引用:如果两个对象相互引用,但是没有其他地方引用它们,那么它们就不能被垃圾收集器回收,导致内存泄漏。

  3. 意外的全局变量:垃圾回收器只会回收动态分配的内存,不能处理静态分配的内存,例如全局变量。

  4. 对象池未正确使用:如果使用对象池时没有正确释放对象,这些对象将继续存在于内存中,导致内存泄漏。

  5. 大量 DOM 元素:如果在页面上创建大量的 DOM 元素,且这些元素没有被正确清理和销毁,可能会导致内存泄漏。

  6. 不正确的缓存机制:如果缓存机制被错误地实现,可能会导致缓存数据一直存在于内存中而无法释放。

JS垃圾回收机制?JS垃圾回收方法?

JavaScript 的垃圾回收机制是自动垃圾回收,它的主要工作就是定期扫描内存中的对象并清除不再使用的对象所占用的内存空间,以避免内存泄漏和浪费。

具体来说,JavaScript 引擎会在变量不再有用时(即没有被任何其他变量或函数引用)将其标记为“可回收”对象。然后,垃圾回收器会定期扫描内存,找到这些可回收对象,并释放他们所占的内存空间。垃圾回收器采用的算法包括标记-清除算法引用计数算法分代回收算法等。

需要注意的是,垃圾回收器只能回收动态分配内存,而不能处理静态分配内存,例如全局变量和闭包等。因此,在编写 JavaScript 代码时,我们应该尽可能避免创建过多的全局变量和闭包,同时及时处理掉不再使用的变量和对象,以便垃圾回收器能够更好的完成垃圾回收工作。

可参考:简单了解 JavaScript 垃圾回收机制
深入理解JavaScript——垃圾回收机制

引用计数算法 和 标记清除算法

引用计数算法
顾名思义,让所有对象实现记录下有多少“程序”在引用自己,让各对象都知道自己的 人气指数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。

优势:

  • 可即刻回收垃圾。当被引用数值为0时,变成垃圾就立刻被回收。
  • 不用去遍历堆里面的所有活动对象和非活动对象。

劣势:

  • 计数器需要占很大的位置。
  • 最大的劣势是无法解决对象互相引用无法回收的问题。因为算法是将 引用次数为 0 的对象销毁,此处都不为 0,是1,导致 垃圾回收器 不会回收他们,那么这就是 内存泄漏 问题。后来这个机制被放弃了。


标记-清除算法
这种垃圾回收方式是一个定时运行的任务,也就是说当程序运行一段时间后,统一垃圾回收;
在 V8 引擎里面,使用最多的就是这种方法。

该算法垃圾回收过程分为两个阶段

  • 标记阶段:把所有活动对象做上标记。
  • 清除阶段:把没有标记(也就是非活动对象)销毁


优势:
实现简单,打标记也就是打或者不打两种可能,所以就一位二进制位就可以表示解决了循环引用问题。
(这种方法可以解决循环引用问题,因为两个对象从全局对象出发无法获取。因此,它们无法被标记,他们将会被垃圾回收器回收)
劣势:
造成碎片化(有点类似磁盘的碎片化)
再分配时遍次数多,如果一直没有找到合适的内存块大小,那么会遍历空闲链表(保存堆中所有空闲地址空间的地址形成的链表)一直遍历到尾端

总结:
对于JS垃圾回收机制,当然是垃圾生成的越少越好,我们可以用一些方法减少垃圾回收,例如手动进行内存释放:

let arr = [a,b,c,d,e,f];
arr.length;        // 将长度设置为0释放内存
let obj = {name: "小明",age: 18,
}
obj = null;        // 清除引用释放内存
// 等等。。。

promise是什么?怎么使用??

Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。

promise是一个对象,从它可以获取异步操作的消息;promise有三种状态: 等待pending,成功fulfilled,失败rejected,当promise状态发生改变,就会触发.then()里的响应函数处理后续步骤;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

当Promise启动后,
满足成功的条件时我们让状态从等待变成成功
满足失败的条件时我们让状态从等待变成失败

应用场景:
1、解决回调地狱 (通过.then .catch方法,处理ajax返回的结果,用链式调用的方式解决回调地狱)
2、将异步操作队列化,按照期望的顺序执行,返回符合预期的结果(.than(1).than(2).than(3) 顺序执行)

Promise使用:

  //拿ajax的例子来做一下Promise封装function myAjax(url) {return new Promise((resolve, reject) => {$.ajax({url: url,success: function(data) {//成功则返回data数据resolve(data)},error: function(err) {//失败则返回错误信息errreject(err)}});})}/*来来来,看好了,封装好ajax之后我们就可以去进行请求数据了*/let res1 = myAjax('xxxx/api/xxxx');let res2 = res1.then(myAjax('xxxx/api/xxxx'));let res3 = res2.then(myAjax('xxxx/api/xxxxx'));

可参考:(前端必会)理解异步和Promise的使用
js promise看这篇就够了

async/await
async/await是 JS 中编写异步或非阻塞代码的新方法。它建立在Promises之上,让异步代码的可读性和简洁度都更高。

为什么代码可读性变得更高了?因为页面上可能有多个promise的.then()方法,导致代码看起来很复杂,此时可以使用 await代替.then(),让代码更少,更简洁(async/await 的优 势:可以很好地处理 then 链)

(1)建立在Promises之上,相对于 Promise 和回调,它的可读性和简洁度都更高
(2)它是基于promise构建的,比如async 定义的函数,返回的还是一个promise对象

可参考:vue中异步函数async和await的用法

js的原型链

(1)简单理解就是原型组成的链,对象的__proto__是它的原型,而原型也是一个对象,也有__proto__属性,原型的__proto__又是原型的原型,就这样可以一直通过__proto__想上找,这就是原型链,当向上找找到Object的原型的时候,这条原型链就算到头了。
(2)当访问对象a的b属性的时候,首先会查找当前对象的b属性,如果没有,然后依次按照prototype往上找直到找到Object.prototype为止,没有则返回undefined,所以说无处不在。
(3)可以类比于冒泡事件,从当前对象开始,通过_proto__属性可以一直找到它的父级,父级也没有__proto__属性,可以继续往上找,直到找到object为止
(4)__proto__是对象的属性。 prototype是函数方法的属性

说一下继承的几种方式及优缺点?

说比较经典的几种继承方式并比较优缺点就可以了

  1. 借用构造函数继承,使用call或apply方法,将父对象的构造函数绑定在子对象上
  2. 原型继承,将子对象的prototype指向父对象的一个实例
  3. 组合继承

原型链继承的缺点
字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。

借用构造函数(类式继承)
借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。

组合式继承
组合式继承是比较常用的一种继承方法,其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。

说一下同步和异步

同步会阻塞,异步不会阻塞

同步:程序运行从上而下,浏览器必须把这个任务执行完毕,才能继续执行下一个任务

异步:程序运行从上而下,浏览器任务没有执行完,但是可以继续执行下一行代码,当被调用者得到结果之后会通过回调函数主动通知调用者。

JS中的异步操作有哪些

1、定时器都是异步操作
2、事件绑定都是异步操作
3、AJAX中一般我们都采取异步操作(也可以同步)
4、回调函数可以理解为异步(不是严谨的异步操作)
剩下的都是同步处理

js延迟加载的方式有哪些?

defer 属性在<script> 元素中设置 defer 属性,等于告诉浏览器立即下载,但延迟执行,只适用于外部脚本文件。
和async属性、动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)
使用setTimeout延迟方法
让JS最后加载(把js外部引入的文件放到页面底部,来让js最后引入,从而加快页面加载速度)

希望获取到页面中所有的checkbox怎么做?(不使用第三方框架)?

var inputs = document.getElementsByTagName("input");//获取所有的input标签对象
var checkboxArray = [];//初始化空数组,用来存放checkbox对象。
for(var i=0;i<inputs.length;i++){var obj = inputs[i];if(obj.type=='checkbox'){checkboxArray.push(obj);}
}

写一个function,清除字符串前后的空格。(兼容所有浏览器)?

String.prototype.trim= function(){return this.replace(/^\s+/,"").replace(/\s+$/,"");}

Cookie在客户机上是如何存储的

Cookies就是服务器暂存放在你的电脑里的文本文件,好让服务器用来辨认你的计算机。当你在浏览网站的时候,Web服务器会先送一小小资料放在你的计算机上,Cookies 会帮你在网站上所打的文字或是一些选择都记录下来。当下次你再访问同一个网站,Web服务器会先看看有没有它上次留下的Cookies资料,有的话,就会依据Cookie里的内容来判断使用者,送出特定的网页内容给你。

cookie和session区别

cookie

Cookie是访问某些网站以后在本地存储的一些网站相关的信息
Cookie中一般包括如下主要内容:

  1. key:设置的cookie的key
  2. value:key对应的value
  3. max_age/expire_time:设置cookie的过期时间
  4. domain:该cookie在哪个域名中有效。一般设置子域名,比如cms.example.com。
  5. path:该cookie在哪个路径下有效

例如:我们登录某一个网站时需要输入用户名和密码,如果用户名和密码保存为cookie,则下次我们登录该网站的时候就不需要在输入用户密码了。

session

session是存在服务器的一种用来存放用户数据的类HashTable结构。
浏览器第一次发送请求时,服务器自动生成了—HashTable和—SessionID来唯一标识这个HashTabe,并将其通过响应发送到浏览器。浏览器第二次发送请求会将前一次服务器响应中的SessionID放在请求中一并发送到浏览器上,服务器从请求中提取出sessionID,并和保存的所有sessionID进行对比,找到这个用户对应的HashTable。

例如:我们浏览一个购物网站,用户将部分商品添加到购物车中,许久以前许多网站都是用服务端session存储购物车内容(现在基本都是用数据库了),就用到了session存储这部分信息。

区别

1. 存储位置不同

  • cookie的数据信息存放在本地。
  • session的数据信息存放在服务器上

2. 存储容量大小不同

  • cookie存储的容量较小,一般 <= 4kb
  • session存储容量大小没有限制(但是为了服务器性能考虑,一般不能存放太多数据)。

3. 存储有效期不同

  • cookie可以长期存储,只要不超过设置的过期时间,可以一直存储
  • session在超过一定的操作时间(通常为30分钟)后会失效,但是当关闭浏览器时,为了保护用户信息,会自动调用session.invalidate()方法,该方法会清除掉session中的信息

4. 安全性不同

  • cookie存储在客户端,所以可以分析存放在本地的cookie并进行cookie欺骗,安全性较低。
  • session存储在服务器上,不存在敏感信息泄露的风险,安全性比较高。

5. 域支持范围不同

  • cookie支持跨域名访问。例如:所有a.com的cookie在a.com下都能用。
  • session不支持跨域访问。例如:www.a.com的session在api.a.com下不能用

6. 对服务器压力不同

  • cookie保存在客户端,不占用服务器资源
  • session是保存在服务器端,每个用户都会产生一个session,session过多的时候会消耗服务器资源,所以大型网站会有专门的session服务器

7. 存储的数据类型不同

  • cookie中只能保管ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据
  • session中能够存储任何类型的数据,包括且不限于string,integer,list,map等

ajax原理

创建一个XHR对象,并发送异步请求,接着监听服务器响应结果,并把它渲染在页面上

什么是json??

(1)JSON 是一种轻量级的数据交换格式;采用键值对的方式,易于阅读和编写,同时也易于机器解析和生成。
(2)JSON 是独立于语言的,也就是说不管是什么语言,都可解析为json,只需按照json规则来就行。
(3)JSON的语法表示三种类型值,简单值(字符串,数值,布尔值,null),数组对象

git使用过程中,如果你在开发着业务,突然另一个分支有一个bug要改,你怎么办

git stash       //将目前还不想提交的但是已经修改的内容进行保存至暂存区
git stash pop   //将所有暂存区的内容取出来
//过程:当正在dev分支上开发某个项目,这时项目中出现一个bug,需要紧急修复,但是正在开发的内容只是完成一半,还不想提交,这时可以用git stash命令将修改的内容保存至堆栈区,然后顺利切换到hotfix分支进行bug修复,修复完成后,再次切回到dev分支,从堆栈中恢复刚刚保存的内容。

解释一下 JavaScript的同源策略

同源策略是客户端脚本(尤其是Javascript)的安全度量标准,为了防止某个文档或脚本从多个不同源装载
同源策略是一种安全协议,指一段脚本只能读取来自同一来源的窗口和文档的属性
所谓同源就是同域名、同协议、同端口,只有同源的地址才能相互通过ajax方式请求

为什么要有同源限制

我们举例说明:比如一个黑客程序,他利用IFrame把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名,密码登录时,他的页面就可以通过Javascript读取到你的表单中input中的内容,这样用户名,密码就轻松到手了。

为什么要组件化开发??

有时候页面代码量太大,逻辑太多或者同一个功能组件在许多页面均有使用,维护起来相当复杂,这个时候,就需要组件化开发来进行功能拆分、组件封装,已达到组件通用性,增强代码可读性,维护成本也能大大降低

组件化开发的优点??

很大程度上降低系统各个功能的耦合性,并且提高了功能内部的聚合性。这对前端工程化及降低代码的维护来说,是有很大的好处的,耦合性的降低,提高了系统的伸展性,降低了开发的复杂度,提升开发效率,降低开发成本

图片的预加载和懒加载

预加载:就是在网页全部加载之前,提前加载图片,当用户需要查看时可直接从本地缓存中渲染,以提供给用户更好的体验,减少等待的时间。
懒加载:延迟加载图片或符合某些条件时才加载某些图片。

两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。预加载则会增加服务器前端压力,懒加载对服务器有一定的缓解压力作用。

对虚拟DOM的理解和优缺点

虚拟DOM的实现原理与优缺点

理解

虚拟 DOM 就是为了解决浏览器性能问题而被设计出来的
它是将整个页面抽象为一个 JavaScript 对象树(虚拟DOM),当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把所记录的差异应用到真正的 dom 树上,视图就更新了。利用DOM diff算法减少不必要的DOM操作和页面重绘,提高性能和用户体验。

虚拟DOM也可以方便实现组件化开发,使得前端代码更具可维护性和可复用性。

优点

1)快速渲染:虚拟 DOM 可以直接操作 JavaScript 对象,无需访问真实浏览器的 DOM。
2)减少不必要的 DOM操作:虚拟 DOM 通过 diff 算法比较前后两个状态的差别,只更新需要改变的部分,减少大量不必要的 DOM 操作,提升性能。
3)跨平台:虚拟 DOM 不依赖具体的浏览器实现,可以在不同的平台上使用,例如服务器渲染,移动端开发等。
4)提高开发效率:虚拟 DOM 可以简化组件的编写和维护,开发效率提高。

缺点

1)需要额外的内存开销:虚拟 DOM 需要创建一颗以 JavaScript 对象表示的虚拟 DOM 树,在一些场景下可能会占用过多的内存。
2)性能瓶颈:虚拟 DOM 在一些复杂场景下可能会导致性能瓶颈,例如大量的列表渲染、频繁的样式变化等。

浏览器渲染机制

详细请点击 浏览器渲染机制

1)处理 HTML 并构建 DOM 树。
2)处理 CSS 构建 CSSOM 树(样式树)。
3)将 DOMCSSOM 合并成一个**渲染树**。
4)根据渲染树来布局,计算每个节点的位置。
5)调用 CPU 绘制,合成图层,显示在屏幕上

第4步和第5步是最耗时的部分,这两步合起来,就是我们通常所说的渲染

浏览器在渲染时的两种现象

重排重绘,重排也叫也叫回流

重排:当 DOM 结构发生变化或者元素样式发生改变时,浏览器需要重新计算元素的几何属性并重新布局,这个过程比较消耗性能。
重绘:指元素的外观样式发生变化(比如改变 color 就叫称为重绘),但是布局没有变,此时浏览器只需要重新绘制元素就可以了,比重排消耗的性能小一些。

重排必定会发生重绘,重绘不一定会引发重排。重排所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列重排

微任务和宏任务

微任务和宏任务是 JavaScript 异步编程中的两个重要概念,它们是为了解决 JavaScript 单线程运行时遇到异步代码所需的执行顺序问题而产生的。

微任务 是指 在当前任务执行完成后立即执行的任务。
宏任务 是指 需要等待事件循环下一轮才能执行的任务。

setTimeout(_ => console.log(4))
new Promise(resolve => {resolve()console.log(1)
}).then(_ => {console.log(3)
})console.log(2)
//执行顺序 1,2,3,4

原因是: 执行顺序: 同步任务 》异步任务;异步任务分为微任务和宏任务,微任务优先级大于宏任务。

nextTick 也是微任务。

CommonJS 和ES6 Module的区别

CommonJS 和 ES6 Module 都是用来组织 JavaScript 代码的模块系统,但它们有以下区别:

  • 语法不同: CommonJS 使用 require() 导入模块,使用 module.exportsexports 输出模块。ES6 Module 使用 import 导入模块,使用 export 输出模块。

  • 加载方式:CommonJS 使用 同步 加载,即在模块内部 require() 一个模块时会等到该模块加载完成后才执行之后的代码;ES6 Module 使用 异步 加载,在编译阶段确定好依赖关系,然后在运行时根据这个依赖关系动态加载模块。。

  • 运行时执行:CommonJS 在运行时执行,每次导入模块都会重新执行一遍模块中的代码。ES6 Module在编译时执行,只会执行一次,然后将结果保存在内存中,多次导入该模块不会重新执行。

  • 静态分析:由于ES6 Module 在编译时执行,所以可以进行静态分析,提高了代码的 可读性和维护性。而CommonJS 的动态加载方式则不能进行静态分析。

  • CommonJS是对模块的浅拷贝,ES6 Module是对模块的引入(引用),即ES6 Module只存只读,不能改变其值,具体点就是指针指向不能变,类似const 。

总的来说,ES6 Module 更加现代化,具有更好的可读性、可维护性和性能优势,但在一些旧的项目或特定场景下,CommonJS 仍然具有重要的作用。

js是单线程,为什么可以处理高并发请求?

尽管 JavaScript 是单线程语言,但浏览器Node.js 这样的环境却是多线程的。当高并发请求到达时,浏览器或 Node.js 会将这些请求放在任务队列中进行排队,并且适时地将这些请求分配给可用的线程进行处理,从而实现高并发请求的处理。此外,开发人员还可以使用异步编程方式(如回调函数Promiseasync/await),使得在等待 I/O 操作完成时,JavaScript 可以继续执行其他操作,避免了线程阻塞,从而更好地处理高并发请求。

js基础面试题整理(包含ES5,ES6)相关推荐

  1. SVG.js 基础图形绘制整理(二)

    一.折线 var draw = SVG('svg1').size(300, 300); //画折线 //使用字符串点 // var polyline=draw.polyline('0,0 100,50 ...

  2. 分享一些常用的 JS 基础面试题

    介绍 此篇属于前端算法入门系列的第一篇,主要介绍常用的数组方法.字符串方法.遍历方法.高阶函数.正则表达式以及相关数学知识. 前端算法入门一:刷算法题常用的JS基础扫盲[1] 前端算法入门二:时间空间 ...

  3. 2019年JS基础面试题

    #JS基础 ###1.javascript的typeof返回哪些数据类型 string number array object function Boolean undefined 数组(Array) ...

  4. 【2022版】 Java基础面试题整理(含答案解析)

    介绍 ​本套面试题内容包含:Java.MyBatis.ZooKeeper.Dubbo.Elasticsearch.Memcached. Redis.MySQL.Spring.Spring Boot.S ...

  5. js基础面试题之手写各种简单方法

    js基础知识点 实现'-'转驼峰式命名 function handerfn(str){let arr = str.split('-');let strResult=''arr.forEach(item ...

  6. JS 基础面试题 2019年最新前端面试题目

    1.js基础 1.eval是做什么的? 它的功能是把对应的字符串解析成JS代码并运行: 应该避免使用eval,不安全,非常耗性能(2次,一次解析成js语句,一次执行). 由JSON字符串转换为JSON ...

  7. 前端基础知识点整理(html+css+js基础)、不包含框架

    css盒子模型 盒模型都是由四个部分组成的,分别是margin.border.padding和content. ​ 标准盒模型和IE盒模型的区别在于设置width和height时,所对应的范围不同.标 ...

  8. 【笑小枫-面试篇】Java基础面试题整理,努力做全网最全

    写在前面 或许你只是想白嫖内容,或许你也会忽略这段文字,但我还是想弱弱的说 题目整理耗费了大量精力,希望可以给博主点赞收藏,谢谢大家啦 我呢,笑小枫,一个努力的普通人,也希望可以花1秒钟记住我一下 也 ...

  9. 2022年js基础面试题---持续更新

    目录 1.javascipt的数据类型 1.1基本类型 1.2引用类型 1.3存储方式的区别 1.4 undefined和null的区别 1.5JavaScript什么情况下会返回undefined值 ...

最新文章

  1. 利用setTimeout方法控制JS中方法的执行顺序
  2. python打印数组中期望元素的位置
  3. linux yum下载RPM包后再安装LAMP环境
  4. Apache+PHP 无法加载 MySql 模块的问题
  5. netty ssl 服务器
  6. 深度学习——夏侯南溪关注的深度学习任务
  7. 创建自定义主机头的网站集
  8. python是什么 自学-初学 Python 者自学 Anaconda 的正确姿势是什么?
  9. effective c++:对象的赋值运算
  10. 北理乐学c语言基础答案晕,北理乐学C语言答案最新.doc
  11. 【解决】Git:hint:Pulling without specifying how to reconclie divergent branches is...
  12. 苹果手机连电脑只显示充电怎么办
  13. UI设计师未来的发展趋势
  14. 韩博士一键重装win8系统的图文步骤
  15. 吉林警方重击涉黑涉恶犯罪 一年打掉439个团伙
  16. 高数考研归纳 - 微分学 - 中值定理
  17. 浅拷贝之copyProperties()
  18. Java @Data注解
  19. oracle ora 02437,给表追加主键-----报错ORA-02437: 无法验证 (DENGCHAO.TEST) - 违反主键
  20. Blood闪烁特效制作

热门文章

  1. 上海交大暑期计算机培训,上海交通大学2018暑期学校报名通知
  2. 闲暇时间我用C语言写出来一个开心消消乐
  3. 支付系统-财务记账 复式记账 借贷
  4. HTML语言中 blur()方法,HTML DOM blur() 方法
  5. 远远被大家低估了的实用智能家居设备——窗帘电机
  6. Mysql,SQL server中英文翻译对照
  7. 屏幕录像功能技术探索及分享
  8. [讨论]什么是网络安全?
  9. 韩信点兵--求分数序列前N项和--特殊a串数列求和--猜数字游戏
  10. mysql数据库怎么导出导入表