最新2024前端面试js题目总结,不妨看看有没有属于你的那道题,CSS浮动实战

总结

  • 框架原理真的深入某一部分具体的代码和实现方式时,要多注意到细节,不要只能写出一个框架。

  • 算法方面很薄弱的,最好多刷一刷,不然影响你的工资和成功率😯

  • 在投递简历之前,最好通过各种渠道找到公司内部的人,先提前了解业务,也可以帮助后期优秀 offer 的决策。

  • 要勇于说不,对于某些 offer 待遇不满意、业务不喜欢,应该相信自己,不要因为当下没有更好的 offer 而投降,一份工作短则一年长则 N 年,为了幸福生活要慎重选择!!!

    开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

喜欢这篇文章文章的小伙伴们点赞+转发支持,你们的支持是我最大的动力!

我们创造的每一个函数都有一个prototype(原型)属性。这个属性是一个指针,指向原型对 象。在默认情况下,所有的原型对象都会有一个constructor(构造函数)属性,这个属性包含一个指向prototype属相所在的指针。当调用构造函数创建一个新实例之后,该实例内部将包含一个指针(内部属性),指向构造函数的原型对象。

https://juejin.im/post/5ae95290518825672c00c0a4

10:异步方式


  1. Promise

  2. Generator

  3. Async-await

  4. Node.js 中的nextTick()和setimmediate()

  5. async库

11:修复bug或添加新功能的常见工作流(git命令)是什么?空和未定义的javascript有什么区别?


git flow feature start f1 添加新特性,这个操作创建了一个基于develop的特性分支,并切换到这个分支之下。

git flow feature finish f1 完成新特性,这个操作会合并f1分支到develop分支,并删除特性分支,切换回develop分支。

git flow feature publish f1 发布新分支,发布新特性分支到远程服务器,其它用户也可以使用这分支。

修复bug:

git flow hotfix start VERSION [BASENAME] 创建hotfix分支,VERSION 参数标记着修正版本,[BASENAME]为finish release时填写的版本号。

12:您使用什么框架来编写单元测试,写下一个案例来验证调用的函数


一、问题描述:

在一个升序数组中,使用折半查找得到要查询的值的索引位置。如:

var a=[1,2,3,4,5,6,7,8,9];

search(a,3);//返回2

search(a,1);//左边界,返回0

search(a,9);//右边界,返回8

search(a,0);//比最小的值还小,返回"您查找的数值不存在"

search(a,10);//比最大的值还大,返回"您查找的数值不存在"

注:折半查找必须在有序数组中才有效,无序的数组不能实现查找功能。比如:在[10,5,6,7,8,9,20]中查找10,中间索引位置的值为7,比较得出7比10小,因而应该在右子数组中查找,实际上不可能找到10;

二、我的实现

function search(arr,num) {

var l=arr.length;

var left=0;

var right=l-1;

var center=Math.floor((left+right)/2);

while(left<=l-1&&right>=0){

if (arr[center]==num) return center;

if (left==right) return “您查找的数不存在”;

if (arr[center]>num) {

right=center-1;

center=Math.floor((left+right)/2);

}else if (arr[center]<num) {

left=center+1;

center=Math.floor((left+right)/2);

}

}

}

var a=[1,2,3,4,5,6,7,8,9];

console.log(search(a,-2));

说明:

1、基本思路:

每次比较,如果数组中间索引位置的值比要查找的值大,就转而在数组中间位置之前的子数组中查找;相反,如果数组中间索引位置的值比要查找的值大,就转而在数组中间位置之后的子数组中查找;如果数组中间索引位置的值恰好等于要查找的值,就返回该索引位置。

2、left定义查找范围的起始位置,right定义查找范围的结束位置,center定义查找范围的中间位置。

3、while中的逻辑说明:

(1)由于不知道具体查找查找多少次,while是比较好的选择;

(2)循环结束条件:

a、一旦当right小于0时,就不再查找,再纠缠也不会有结果。例如:在a=[1,2,3,4,5,6,7,8,9]中查找0,当查找范围变为left=0,right=0,center=0时,进入while语句,由于arr[center]>0,故执行

right=center-1;center=Math.floor((left+right)/2);

得到right=-1此时应不再进入循环;

b、一旦当left>l-1时,就不再查找,同样再纠缠也不会有结果。例如:在a=[1,2,3,4,5,6,7,8,9]中查找10,当查找范围变为left=8,right=8,center=8时,进入while语句,由于arr[center]<10,故执行

left=center;center=Math.floor((left+right)/2);

得到left=9,此时应不再进入循环;

4、始终是通过center匹配到要查找的值;

5、Math.floor处理了查找范围长度为偶数的情况;

6、当left==right了,而arr[center]==num却没执行,可以得出结论查找不到的;

7、当arr[center]==num时,整个函数都结束了,后面语句是不会执行的。

13.编写一个regex表达式以查找内容,内容以2个数字开头,以结尾


var reg = /1[0-9]$/

14.push 添加数组后, 是怎么响应的


push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度

注释:该方法会改变数组的长度。

语法:

arrayObject.push(newelement1,newelement2,…,newelementX)

参数描述

newelement1 必需。要添加到数组的第一个元素。

newelement2 可选。要添加到数组的第二个元素。

newelementX 可选。可添加多个元素。

push() 方法可把它的参数顺序添加到 arrayObject 的尾部。它直接修改 arrayObject,而不是创建一个新的数组。push() 方法和 pop() 方法使用数组提供的先进后出栈的功能。

15.js基本数据类型


Undefined、Null、Bollean、Number、String

16.js中和=的区别是什么


前者会自动转换类型

后者不会

17.for和for in 区别


语法结构上不同,

for 一般用来遍历数组的,是比较简单的操作

for in 一般用来遍历对象,虽然for in 也能遍历数组,但是会存在

以下几个问题:

1、index索引为字符串型数字,不能直接进行几何运算

2、遍历顺序有可能不是按照实际数组的内部顺序

3、使用for in会遍历数组所有的可枚举属性,包括原型。例如上栗

的原型方法method和name属性

这也是为什么用for不用for in的区别,如果是遍历普通数组的话,

用for是最好的选择,但是如果是对象,用for in就好了。

18.js中和=的区别是什么


操作数1 == 操作数2, 操作数1 === 操作数2

双等号==:

(1)如果两个值类型相同,再进行三个等号(===)的比较

(2)如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:

1)如果一个是null,一个是undefined,那么相等

2)如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较

三等号===:

(1)如果类型不同,就一定不相等

(2)如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相 等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)

(3)如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。

(4)如果两个值都是true,或是false,那么相等

(5)如果两个值都引用同一个对象或是函数,那么相等,否则不相等

(6)如果两个值都是null,或是undefined,那么相等

19:for和for in 区别


for in:

1.for…in 语句用于对数组或者对象的属性进行循环操作。

2.for … in 循环中的代码每执行一次,就会对数组的元素或者对象的属性进行一次操作。

3.for…in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。

for :

1.for循环是对数组的元素进行循环,而不能引用于非数组对象。

20:数组去重的方法


第一种:

function uniq(array){

var temp = []; //一个新的临时数组

for(var i = 0; i < array.length; i++){

if(temp.indexOf(array[i]) == -1){

temp.push(array[i]);

}

}

return temp;

}

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

console.log(uniq(aa));

第二种:对象键值法去重

function uniq(array){

var temp = {}, r = [], len = array.length, val, type;

for (var i = 0; i < len; i++) {

val = array[i];

type = typeof val;

if (!temp[val]) {

temp[val] = [type];

r.push(val);

} else if (temp[val].indexOf(type) < 0) {

temp[val].push(type);

r.push(val);

}

}

return r;

}

var aa = [1,2,“2”,4,9,“a”,“a”,2,3,5,6,5];

console.log(uniq(aa));

第三种:排序后相邻去除法

function uniq(array){

array.sort();

var temp=[array[0]];

for(var i = 1; i < array.length; i++){

if( array[i] !== temp[temp.length-1]){

temp.push(array[i]);

}

}

return temp;

}

var aa = [1,2,“2”,4,9,“a”,“a”,2,3,5,6,5];

console.log(uniq(aa));

第四种:数组下标法

function uniq(array){

var temp = [];

for(var i = 0; i < array.length; i++) {

//如果当前数组的第i项在当前数组中第一次出现的位置是i,才存入数组;否则代表是重复的

if(array.indexOf(array[i]) == i){

temp.push(array[i])

}

}

return temp;

}

var aa = [1,2,“2”,4,9,“a”,“a”,2,3,5,6,5];

console.log(uniq(aa));

第五种:优化遍历数组法

function uniq(array){

var temp = [];

var index = [];

var l = array.length;

for(var i = 0; i < l; i++) {

for(var j = i + 1; j < l; j++){

if (array[i] === array[j]){

i++;

j = i;

}

}

temp.push(array[i]);

index.push(i);

}

console.log(index);

return temp;

}

var aa = [1,2,2,3,5,3,6,5];

console.log(uniq(aa));

21:排序的方法


第一种:冒泡排序:

var arr = [1,4,-8,-3,6,12,9,8];

function bubbleSort(arr){

for (var i = 0; i < arr.length; i++) {

for (var j = 0; j < arr.length-i-1; j++) {

if(arr[j] > arr[j+1]){

var c = arr[j];

arr[j] = arr[j+1];

arr[j+1] = c;

}

}

}

return arr;

}

console.log(bubbleSort(arr));

快速排序:

var arr = [1,4,-8,-3,6,12,9,8];

function quicksort(arr){

if(arr.length <= 1){

return arr;

}

var middleIndex = Math.floor(arr.length/2);

var middleNum = arr.splice(middleIndex,1);

var left = [], right = [];

for (var i = 0; i < arr.length; i++) {

if(arr[i] < middleNum){

left.push(arr[i]);

} else {

right.push(arr[i]);

}

}

return quicksort(left).concat(middleNum, quicksort(right));

}

console.log(quicksort(arr));

选择排序:

var arr = [1,4,-8,-3,6,12,9,8];

function selectSort(arr){

for(var i=0;i<arr.length;i++){

//设置当前范围最小值和索引

var min = arr[i];

var minIndex = i;

//在该范围选出最小值

for(var j=i+1;j<arr.length;j++){

if(min>arr[j]){

min = arr[j];

minIndex = j;

}

}

//将最小值插入,并将原来位置的最小值删除

arr.splice(i,0,min);

arr.splice(minIndex+1,1);

}

return arr;

}

console.log(selectSort(arr));

插入排序:

var array = [1,4,-8,-3,6,12,9,8];

function selectSort(arr){

for(var i=0;i<arr.length;i++){

//设置当前范围最小值和索引

var min = arr[i];

var minIndex = i;

//在该范围选出最小值

for(var j=i+1;j<arr.length;j++){

if(min>arr[j]){

min = arr[j];

minIndex = j;

}

}

//将最小值插入,并将原来位置的最小值删除

arr.splice(i,0,min);

arr.splice(minIndex+1,1);

}

}

selectSort(array);

document.write(array);

22:冒泡排序


var arr = [1,4,-8,-3,6,12,9,8];

function bubbleSort(arr){

for (var i = 0; i < arr.length; i++) {

for (var j = 0; j < arr.length-i-1; j++) {

if(arr[j] > arr[j+1]){

var c = arr[j];

arr[j] = arr[j+1];

arr[j+1] = c;

}

}

}

return arr;

}

console.log(bubbleSort(arr));

23:原型链的理解:


在谈原型链之前,我们首先要了解自定义函数与 Function 之间是什么关系,而构造函数、原型和实例之间又存在什么千丝万缕的关系呢?其实,所有的函数都是 Function 的实例。在构造函数上都有一个原型属性 prototype,该属性也是一个对象;那么在原型对象上有一个 constructor 属性,该属性指向的就是构造函数;而实例对象上有一个 proto 属性,该属性也指向原型对象,并且该属性不是标准属性,不可以用在编程中,该属性用于浏览器内部使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QqhVpWo7-1630416218424)(en-resource://database/450:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8tp8hnPS-1630416218430)(en-resource://database/452:1)]

1、原型链

1)构造函数、原型和实例的关系

①构造函数都有一个属性prototype,这个属性是一个对象(Object的实例)

②原型对象prototype里面有一个constructor属性,该属性指向原型对象所属的构造函数

③实例对象都有一个_proto_属性,该属性也指向构造函数的原型对象,它是一个非标准属性,

不可以用于编程,它是用于浏览器自己使用的

2)prototype与_proto_的关系

①prototype是构造函数的属性

②_proto_是实例对象的属性 ——这两者都指向同一个对象

【总结】

i)函数也是对象,对象不一定是函数;

ii)对象的本质:无序的键值对集合;键值对当中的值可以是任意数据类型的值

iii)对象就是一个容器,这个容器当中放的是(属性和方法)

3)属性搜索

①在访问对象的某个成员的时候会先在对象中找是否存在

②如果当前对象中没有就在构造函数的原型对象中找

③如果原型对象中没有找到就到原型对象的原型上找

④知道Object的原型对象的原型是null为止

2、Function——

所有函数都是Function的实例

`①本地对象:独立于宿主环境(浏览器)的对象——包括Object、Array、Date、RegExp、 Function、Error、Number、String、Boolean

②内置对象——包括Math、Global(window,在js中就是全局变量),使用的时候不需要 new

③宿主对象——包括自定义对象、DOM、BOM

24:改变this指向的方法


第一种: new关键字改变this指向

function Fn(){

this.user = “追梦子”;

}

var a = new Fn();

console.log(a.user); //追梦子

用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份第二种: call()

第二种: call()

var a = {

user:“追梦子”,

fn:function(){

console.log(this.user); //追梦子

}

}

var b = a.fn;

b.call(a); //若不用call,则b()执行后this指的是Window对象

把b添加到第一个参数的环境中,简单来说,this就会指向那个对象。

第三种:apply()

var a = {

user:“追梦子”,

fn:function(){

console.log(this.user); //追梦子

}

}

var b = a.fn;

b.apply(a);

第四种:bind()

var a = {

user:“追梦子”,

fn:function(){

console.log(this.user);

}

}

var b = a.fn;

b.bind(a); //代码没有被打印

我们发现代码没有被打印,对,这就是bind和call、apply方法的不同,实际上bind方法返回的是一个修改过后的函数。

25:es6新特性


  1. 变量声明

let 与 const:

可以把let看成var,只是它定义的变量被限定在了特定范围内才能使用,而离开这个范围则无效。const则很直观,用来定义常量,即无法被更改值的变量。

for (let i=0;i<2;i++)console.log(i);//输出: 0,1

console.log(i);//输出:undefined,严格模式下会报错

2.类的支持

ES6中添加了对类的支持,引入了class关键字(其实class在JavaScript中一直是保留字,目的就是考虑到可能在以后的新版本中会用到,现在终于派上用场了)。JS本身就是面向对象的,ES6中提供的类实际上只是JS原型模式的包装。现在提供原生的class支持后,对象的创建,继承更加直观了,并且父类方法的调用,实例化,静态方法和构造函数等概念都更加形象化。

//类的定义

class Animal {

//ES6中新型构造器

constructor(name) {

this.name = name;

}

//实例方法

sayName() {

console.log('My name is '+this.name);

}

}

//类的继承

class Programmer extends Animal {

constructor(name) {

//直接调用父类构造器进行初始化

super(name);

}

program() {

console.log(“I’m coding…”);

}

}

//测试我们的类

var animal=new Animal(‘dummy’),

wayou=new Programmer(‘wayou’);

animal.sayName();//输出 ‘My name is dummy’

wayou.sayName();//输出 ‘My name is wayou’

wayou.program();//输出 ‘I’m coding…’

3.字符串模板

字符串模板相对简单易懂些。ES6中允许使用反引号 ` 来创建字符串,此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraible}。如果你使用过像C#等后端强类型语言的话,对此功能应该不会陌生。

//产生一个随机数

var num=Math.random();

//将这个数字输出到console

console.log(your num is ${num});

4.解构:

自动解析数组或对象中的值。比如若一个函数要返回多个值,常规的做法是返回一个对象,将每个值做为这个对象的属性返回。但在ES6中,利用解构这一特性,可以直接返回一个数组,然后数组中的值会自动被解析到对应接收该值的变量中。

var [x,y]=getVal(),//函数返回值的解构

[name,age]=[‘wayou’,‘male’,‘secrect’];//数组解构

function getVal() {

return [ 1, 2 ];

}

console.log(‘x:’+x+‘, y:’+y);//输出:x:1, y:2

console.log(‘name:’+name+‘, age:’+age);//输出: name:wayou, age:secrect

5.Promise:

Promises是处理异步操作的一种模式,之前在很多三方库中有实现,比如jQuery的deferred 对象。当你发起一个异步请求,并绑定了.when(), .done()等事件处理程序时,其实就是在应用promise模式。

//创建promise

var promise = new Promise(function(resolve, reject) {

// 进行一些异步或耗时操作

if ( /*如果成功 */ ) {

resolve(“Stuff worked!”);

} else {

reject(Error(“It broke”));

}

});

//绑定处理程序

promise.then(function(result) {

//promise成功的话会执行这里

console.log(result); // “Stuff worked!”

}, function(err) {

//promise失败会执行这里

console.log(err); // Error: “It broke”

});

26.promise的理解


​ ES6提供的解决异步处理方法

​ 有两个优点

​ 1.promise对象的状态不受外界影响

​ -pending 初始状态

​ -fulfilled 成功状态

​ -rejected 失败状态

​ 2.promise的状态一旦改变,就不会再变,状态不可逆,只能由pending变成pending变成fulfilled或者由pending变成rejected

​ 三个缺点

​ 1.无法取消promise,一旦新建它就会立即执行,无法中途取消

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

​ 3.当处于pending状态时,无法得知目前进展到哪一个阶段

​ 用法

const promise = new Promise(function(resolve, reject) {

// … some code

if (/* 异步操作成功 */){

resolve(value);

} else {

reject(error);

}

});

27.同源策略


​ 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。同源指的是协议,域名和端口号均相同则属于同源

28.前端跨域的方式


​ 使用jsonp跨域,因为script标签引入的js是不受同源策略的限制,通过script标签引入一个js或者是一个其他后缀形式(如php,jsp等)的文件,此时文件返回一个JS函数的调用

​ 通过cors跨域,实现cors通信的关键是服务器,只要服务器实现cors接口,就可以跨域

​ 反向代理跨域,反向代理指的是在前端的服务器环境中, 短暂的开启一个后端服务器, 由后端服务器进行数据请求, 然后在将结果返回给前端

29.AMD,CMD模块化


模块化的开发方式可以提高代码复用率,方便进行代码的管理。通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数。目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统

1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块

2、CMD推崇就近依赖,只有在用到某个模块的时候再去require

这种区别各有优劣,只是语法上的差距,而且requireJS和SeaJS都支持对方的写法

AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同

30.报表绘图类的框架


highcharts http://www.highcharts.com/

jscharts http://www.jscharts.com/

​ AdminLTE http://adminlte.la998.com/

31.库和插件的源代码


​ 库和框架都是一种有别于软件、面向程序开发者的产品形式。

库是将代码集合成的一个产品,供程序员调用。面向对象的代码组织形式而成的库也叫

类库。

框架则是为解决一个(一类)问题而开发的产品,框架用户一般只需要使用框架提供的类

或函数,即可实现全部功能。

32.函数柯里化是什么 ?


柯里化(英语:Currying),又称为部分求值,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回一个新的函数的技术,新函数接受余下参数并返回运算结果。

具体内容见 https://juejin.im/entry/58b316d78d6d810058678579

33.哪些情况下会造成内存泄漏 ?


1)意外的全局变量引起的内存泄露

function leak () {

leak=“xxx”; //leak成为一个全局变量,不会被回收 相当于 window.leak = ‘XXX’

}

  1. 闭包可以维持函数内局部变量,使其得不到释放。

  2. 没有清理的DOM元素引用

  3. 被遗忘的定时器或者回调

34.性能优化 ?


  1. 定义局部变量.查找局部变量比全局变量要快。

  2. 不滥用闭包。

  3. 合并js文件,减少http请求

  4. 避免使用for-in循环

  5. 尽量不用with,eval语句,try-catch的catch子句要谨慎使用

35.工作中闭包的使用案例?使用过什么闭包工具库嘛


  1. 闭包经典使用场景一:通过循环给页面上多个dom节点绑定事件

Button0

Button1

Button2

Button3

Button4

for(var i = 0, len = btns.length; i < len; i++) {

(function(i) {

btns[i].onclick = function() {

alert(i);

}

}(i))

}

  1. 封装变量 闭包可以将一些不希望暴露在全局的变量封装成“私有变量”。

  2. 闭包使用场景三:延续局部变量的寿命

闭包工具库:???

36.301 302如何重定向 ?


301 redirect: 301 代表永久性转移(Permanently Moved)

302 redirect: 302 代表暂时性转移(Temporarily Moved )

详细来说,301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于。301表示旧地址A的资源已经被永久地移除(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。

37.暂时性死区 ?


举例:

console.log (a) //由于变量提升,输出undefined

var a

console.log(a) //报错 ReferenceError: a is not defined

let a

ES6规定,let/const 命令会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。

总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。

这在语法上,称为 “暂时性死区”( temporal dead zone,简称 TDZ)。

38.堆和栈


  • 栈(stack):为自动分配的内存空间,他由系统自动释放。存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配,是直接按值存放的,所以可以直接访问。基本的数据类型放在栈中

  • 堆(heap):则是动态分配的内存,大小不定也不会自动释放。引用类型(object)是存放在堆内存中的,变量实际上是一个存放在栈内存的指针,这个指针指向堆内存中的地址。每个空间大小不一样,要根据情况进行特定的分配。

39.箭头函数的this指向


  • 箭头函数默认不会使用自己的this,而是会和外层的this保持一致,最外层的this就是window对象。在多层对像嵌套里箭头函数里this是和最最外层保持一致的

40.深拷贝和浅拷贝的区别


  • 浅拷贝:只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存

  • 深拷贝:复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。

41.手写一个promise,采用es5的方面。promise的源码理解


var fn=function(resolve, reject){

console.log(‘begin to execute!’);

var number=Math.random();

if(number<=0.5){

resolve(‘less than 0.5’);

}else{

reject(‘greater than 0.5’);

}

}

var p=new Promise(fn);

p.then(function(data){

console.log('resolve: ', data);

}, function(data){

console.log('reject: ', data);

})

对promise源码的理解:

当我们运行 var p=new Promise(fn) 这条语句的时候,fn函数就已经执行了,然而,p.then这个方法是在后面才定义了resolve和reject,那么为何fn函数能够知道resolve和reject函数是什么呢?换句话说,resolve和reject函数是如何回到过去,出现在先执行的fn函数当中的呢?要解决这个问题,主要运用的就是setTimeout这个方法,来延迟fn当中resolve和reject的执行。我们知道js是单线程+消息队列,必须等主线程代码执行完毕才能开始执行消息队列当中的代码。因此,会首先执行then这个方法,给里面两个参数赋值。

加入状态:pending, resolved, rejected

在Promise规范当中,规定Promise只能从初始pending状态变到resolved或者rejected状态,是单向变化的,也就是说执行了resolve就不会再执行reject,反之亦然。并在必要的地方进行判断,防止重复执行。

function MyPromise(fn) {

this.value;

this.status = ‘pending’;

this.resolveFunc = function() {};

this.rejectFunc = function() {};

fn(this.resolve.bind(this), this.reject.bind(this));

}

MyPromise.prototype.resolve = function(val) {

var self = this;

if (this.status == ‘pending’) { //判断状态

this.status = ‘resolved’;

this.value=val;

setTimeout(function() {

self.resolveFunc(self.value);

}, 0);

}

}

MyPromise.prototype.reject = function(val) { //判断状态

var self = this;

if (this.status == ‘pending’) {

this.status = ‘rejected’;

this.value=val;

setTimeout(function() {

self.rejectFunc(self.value);

}, 0);

}

}

MyPromise.prototype.then = function(resolveFunc, rejectFunc) {

this.resolveFunc = resolveFunc;

this.rejectFunc = rejectFunc;

}

链式调用:

要实现链式调用,then方法的返回值也必须是一个Promise对象,这样才能再次在后面调用then。

42.async与promise的区别:


  • 在函数前有一个关键字asyncawait关键字只能在使用async定义的函数中使用。任何一个async函数都会隐式返回一个promise,并且promise resolve 的值就是 return 返回的值

  • 不能在函数开头使用await

  • Async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。

  • Promise 的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(thencatch等等),操作本身的语义反而不容易看出来。

43.async是promise暴露出来的语法糖?


  • async函数就是generator函数的语法糖

44.箭头函数与function的区别?


(1) 箭头函数与function定义函数的写法:

//function

function fn(a, b){

return a + b;

}

//arrow function

var foo = (a, b)=>{ return a + b };

(2) this的指向:

使用function定义的函数,this的指向随着调用环境的变化而变化的,而箭头函数中的this指向是固定不变的,一直指向的是定义函数的环境。

(3) 构造函数

function是可以定义构造函数的,而箭头函数是不行的。

(4) 变量提升

由于js的内存机制,function的级别最高,而用箭头函数定义函数的时候,需要var(let const定义的时候更不必说)关键词,而var所定义的变量不能得到变量提升,故箭头函数一定要定义于调用之前!

45.箭头函数中没有this对象,this是最近的this


(1) 由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值

(2)方法的箭头函数this指向全局window对象,而普通函数则指向调用它的对象,箭头函数没有this

46.箭头函数可以作为构造函数吗?


因为箭头函数没有自己的this,它的this其实是继承了外层执行环境中的this,且this指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则用new调用时会报错!

47.箭头函数替代arguments的方法?


箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是外层局部(函数)执行环境中的值。可以在箭头函数中使用rest参数代替arguments对象,来访问箭头函数的参数列表

文末

篇幅有限没有列举更多的前端面试题,小编把整理的前端大厂面试题PDF分享出来,一共有269页

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值