前端常见面试题少量总结

240 阅读12分钟

原笔记地址

1、什么是Promise?

Promise可以帮助我们更好地处理异步操作。

new Promise((resolve, reject) =>
  {
     setTimeout(() =>
      {
          resolve('result');
      }, 100)
  })
  .then(console.log)
  .catch(console.error);
  

2、async awite 是什么?运行时机呢

    async function testSometing () {
      console.log('2执行testSometing')
      return 'testSometing'
    }
    async function testAsync () {
      console.log('6执行testAsync')
      return Promise.resolve('hello async')
    };
    async function test () {
      console.log('1 test start...')
      const v1 = await testSometing()
      console.log(v1 + '5') // 5
      const v2 = await testAsync()
      console.log(v2 + '8') // 8
      console.log(v1, v2 + '9') // 9
    };
    test()
    let promise = new Promise(resolve => {
      const a = {}
      const b = JSON.parse(a)
      console.log('3promise start..')
      resolve(b)
    })
    promise.then(val => console.log(val)).catch(err =>      console.log(err)) // 7
    console.log('4test end...')
熟悉promise 和 async/awite 异同:
都是解决js异步回调问题,promise内部无法进行try catch 只能使用原型上的catch方式统一进行代码捕获,而且promise内部无法进行断点调试。 async/awite 内部可以try catch可以断点。

3、ES6 set map 函数

const set = new Set([1, 2, 3, 4, 4]);
[...set]

上面代码通过add方法向 Set 结构加入成员,结果表明Set 结构不会添加重复的值

数组去重:
方法一:
[...new Set(array)]
方法二:
function dedupe(array) {
  return Array.from(new Set(array));
}
 
dedupe([1, 1, 2, 3]) // [1, 2, 3]

将类数组对象转换成数组:

let arrayLike = {
    0: 'tom', 
    1: '65',
    2: '男',
    3: ['jane','john','Mary'],
    'length': 4
}
let arr = Array.from(arrayLike)
console.log(arr) // ['tom','65','男',['jane','john','Mary']]
要将一个类数组对象转换为一个真正的数组,必须具备以下条件:
1、该类数组对象必须具有length属性,用于指定数组的长度。如果没有length属性,那么转换后的数组是一个空数组。
2、该类数组对象的属性名必须为数值型或字符串型的数字

ps: 该类数组对象的属性名可以加引号,也可以不加引号

3.1 set 方法


add(value):添加某个值,返回Set结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值

keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
扩展运算符(...)内部使用for...of循环,所以也可以用于 Set 结构。
使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
 
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
 
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
 
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));




3.2 MAP方法:

Map有size()属性,查看Map对象大小,set(key , value) , get(Key), delete(key) , has(key) ,clear()方法。
遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。

keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。

forEach():遍历 Map 的所有成员。

4、 闭包、原型链等概念的理解

4.1、什么是闭包


《高级程序设计》上,这样说:当在函数内部定义了其他函数时候,就创建了闭包。闭包有权访问包含函数内部的所有变量。通俗的说,闭包就是作用域范围,因为js是函数作用域,所以函数就是闭包.更多的应用其实是在内嵌函数,这就会涉及到内嵌作用域,也叫作用域链.如果子作用域里的函数引用了父作用域的函数,这就会带来另外一个问题,什么时候引用结束?如果不结束,就会一直占用内存,过多引用会引起内存泄漏。

4.2、对原型链的理解

当调用某种方法或查找某种属性时,首先会在自身调用和查找,如果自身并没有该属性或方法,则会去它的__proto__属性中调用查找,也就是它构造函数的prototype中调用查找。
由于__proto__是任何对象都有的属性,而js里万物皆对象,所以会形成一条__proto__连起来的链条,递归访问__proto__必须最终访问到头,并且值为null。

1.查找属性,如果本身没有,则会去__proto__中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有__proto__,那么会去它的显式原型中查找,一直到null,如果没有则返回undefined

2.p.proto.constructor  == function Person(){}

3.p._proto.proto== Object.prototype

4.proto

5.通过__proto__形成原型链而非protrotype

利用原型让一个引用类型继承另一个引用类型的属性和方法。

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针(constructor),而实例对象都包含一个指向原型对象的内部指针(__proto__)。
如果让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针(__proto__),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是另一个类型的实例……这就构成了实例与原型的链条。


图解:

图片
图片

5 、JS继承的实现方式?

拷贝自www.cnblogs.com/humin/p/455…文章。

// 定义一个动物类
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    console.log(this.name + '正在睡觉!');
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + '正在吃:' + food);
};

5.1 原型链继承


function Cat(){ 
}
Cat.prototype = new Animal(); // 关键代码
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name); // cat
console.log(cat.eat('fish')); // cat正在吃:fish
console.log(cat.sleep());//cat正在睡觉!
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

特点:
非常纯粹的继承关系,实例是子类的实例,也是父类的实例
父类新增原型方法/原型属性,子类都能访问到
简单,易于实现

缺点:
要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
无法实现多继承
来自原型对象的引用属性是所有实例共享的
创建子类实例时,无法向父类构造函数传参


5.2、构造继承

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name); // Tom
console.log(cat.sleep()); // Tom正在睡觉!
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true
特点:
解决了原型链继承中,子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)

缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性/方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

5.3、实例继承

function Cat(name){
  var instance = new Animal();
  instance.name = name || 'Tom';
  return instance;
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false

特点:
不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果

缺点:
实例是父类的实例,不是子类的实例
不支持多继承


5.4、拷贝继承

function Cat(name){
  var animal = new Animal();
  for(var p in animal){
    Cat.prototype[p] = animal[p];
  }
  Cat.prototype.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

特点:
支持多继承

缺点:
效率较低,内存占用高(因为要拷贝父类的属性)
无法获取父类不可枚举的方法(不可枚举方法,不能使用for in 访问到)

5.5、组合继承

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();

// 组合继承也是需要修复构造函数指向的。

Cat.prototype.constructor = Cat;

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

特点:
弥补了 构造继承 的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
既是子类的实例,也是父类的实例
不存在引用属性共享问题
可传参
函数可复用

缺点:
调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

6.6 、寄生组合继承

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
  Cat.prototype.constructor = Cat;

})();

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true

6、常用js方法

6.1 数组去重

数组去重:
//方法一:
Array.prototype.unique = function(){
    var result = [];
    this.forEach(function(v){
        if(result.indexOf(v) < 0){
            result.push(v);
        }
    });
    return result;
}
//方法二:
Array.prototype.unique = function(){
    var result = [],hash = {};
    this.forEach(function(v){
        if(!hash[v]){
          hash[v] = true;
          result.push(v);
        }
    });
    return result;
}
//方法二存在隐式转换比如 1 和 "1" 会被认为是重复,所以进行升级:
Array.prototype.unique = function(){
    var result = [],hash = {};
    this.forEach(function(v){
        var type = typeof(v);  //获取元素类型
        hash[v] || (hash[v] = new Array());
        if(hash[v].indexOf(type) < 0){
            hash[v].push(type);  //存储类型
            result.push(v);
        }
    });
    return result;
}
//方法四:先排序再去重
Array.prototype.unique = function(){
  var result = [this[0]];
  this.sort();
  this.forEach(function(v){
    v != result[result.length - 1] && result.push(v); //仅与result最后一个元素比较
  });
}
// es6 去重:方法一
Array.prototype.unique = function(){
   return [...new Set(this)];
}
// es6 去重:方法二
Array.prototype.unique = function(){
  return Array.form(new Set(this))
}

//排序算法

6.2、 计算出下面字符串出现次数最多的值?

let str = 'abcdefghigakgjisaaaganghegsa'
function getMaxVal(str) {
  let strArr = [...str]
  let max = 0
  let obj = {}
  let maxVal = ''
  strArr.forEach(function(item, i){
    obj[item] = obj[item] == 'undefined'? 1 : obj[item] + 1
    if(obj[item] > max) {
      max = obj[item]
      maxVal = item
    }
  })
  return maxVal
}

6.3、 翻转字符串

let str = 'hello lsj'
function reverseStr (str) {
  return [...str].reverse().join('')
}

6.4、 翻转字符串

let str = 'hello lsj'
function reverseStr (str) {
  return [...str].reverse().join('')
}

7、async和defer的区别

都是为script增减属性,它定义脚本是否异步执行。async 属性仅适用于外部脚本(只有在使用 src 属性时)有多种执行外部脚本的方法:

· 如果 async="async":脚本相对于页面的其余部分异步地执行(当页面继续进行解析时,脚本将被执行)

· 如果不使用 async 且 defer="defer":脚本将在页面完成解析时执行

· 如果既不使用 async 也不使用 defer:在浏览器继续解析页面之前,立即读取并执行脚本

· 为html元素增加manifest,开发离线web应用程序时他与API结合使用,定义一个URL,在这个URL上描述文档的缓存信息。

· 为iframe增加撒个属性,sandbox、seamless、srcdoc。用来提高页面安全性,防止不信任的web页面执行某些操作。

8、前端缓存机制和办法

9、node面试题

9.1、Cookies如何防范XSS攻击

XSS(Cross-Site Scripting,跨站脚本攻击)是指攻击者在返回的HTML中插入JavaScript脚本。为了减轻这些攻击,需要在HTTP头部配置set-cookie:

HttpOnly - 这个属性可以防止cross-site scripting,因为它会禁止Javascript脚本访问cookie。
secure - 这个属性告诉浏览器仅在请求为HTTPS时发送cookie。

10、js基础查漏补缺。

参考https://www.cnblogs.com/meteorcn/p/node_mianshiti_interview_question.html

10.1、apply, call和bind有什么区别?

三者都可以把一个函数应用到其他对象上,注意不是自身对象也就是用来改变this指向.apply,call是直接执行函数调用,bind是绑定,执行需要再次调用.apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表

10.2、** caller, callee和arguments分别是什么?**

caller, callee之间的关系就像是employer和employee之间的关系,就是调用与被调用的关系,二者返回的都是函数对象引用.arguments是函数的所有参数列表,它是一个类数组的变量.

10.3、defineProperty, hasOwnProperty, isEnumerable都是做什么用的?

Object.defineProperty(obj, prop, descriptor)用来给对象定义属性,有value,writable,configurable,enumerable,set/get等.
hasOwnProerty用于检查某一属性是不是存在于对象本身,继承来的父亲的属性不算.isEnumerable用来检测某一属性是否可遍历,也就是能不能用for..in循环来取到

10.4、列举数组相关的常用方法

push/pop, 
shift/unshift, 
split/join, 
slice/splice/concat, 
sort/reverse, 
map/reduce, 
forEach, filter

10.5、 列举字符串相关的常用方法

 indexOf/lastIndexOf/charAt, 
 split/match/test,
 slice/substring/substr, 
 toLowerCase/toUpperCase
enctype="multipart/form-data" method="post"

10.6、 文件上传需要设置什么?

 
enctype="multipart/form-data" method="post"

10.7、js排序方法总结。

js排序方法汇总.js

10.8、 字符串扩展了哪些方法?

1.codePointAt() 参数:字符在字符串中的位置;返回值:返回一个字符的码点

2.String.fromCodePoint() 参数:码点 返回值:码点对应的字符

3.for....of     字符串的遍历器接口

4.nomalize() 功能:将字符的不同表示方法统一为同样的形式,称之为Unicode正规化。参数:指定normalize的方式。

5.includes()功能:字符串中是否含有子串; 参数:要查找的子串 ;返回值:布尔值,truefalse。

6.startsWith()返回值:布尔类型,表示字符串是否在源字符串的头部;当有第二个参数时,表示开始搜索的位置。

7.endsWith()返回值:布尔类型,表示字符串是否在源字符串的尾部。当有第二个参数时,表示开始搜索的位置。

8.repeat():返回值:将源字符串重复n次。参数:字符串重复次数。

9.padStart()功能:如果字符串不够指定长度,会在头部补全。参数:第一个指定长度,第二个指定字符串。返回值:返回新的字符串

10.padEnd()同上,不过是在尾部

11.String.raw() 功能:用来充当模板字符串的处理函数。返回值:斜杠都被转义的字符串。


JavaScript 异步、栈、事件循环、任务队列

图片
Js 中,有两类任务队列:宏任务队列(macro tasks)和微任务队列(micro tasks)。宏任务队列可以有多个,微任务队列只有一个。那么什么任务,会分到哪个队列呢?

  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering.
  • 微任务:process.nextTick, Promise, Object.observer, MutationObserver.

浏览器的 Event Loop

浏览器的 Event Loop 遵循的是 HTML5 标准,而 NodeJs 的 Event Loop 遵循的是 libuv。 区别较大.


网站性能优化中有一条:dns与解析如