前端面试题之js

1.js基本数据类型

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

引用数据类型:对象(Object)、数组(Array)、函数(Function)。

**注:**Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。

基本类型的值存在栈中,引用数据类型的地址存在栈中,内容存在堆中。

2.堆和栈有什么区别?

堆和栈的概念存在于数据结构中和操作系统内存中。

在数据结构中,栈中数据的存取方式为先进后出。而堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。完全二叉树是堆的一种实现方式。

在操作系统中,内存被分为栈区和堆区。

栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆区内存一般由程序员分配释放,若程序员不释放,程序结束时可能由垃圾回收机制回收。

3.js作用域

全局作用域:在全局定义的变量

局部作用域:在函数内定义的变量

块级作用域:用let声明的变量

4.var,let,const的区别

var声明的变量存在变量提升

let 声明的变量只在 let 命令所在的代码块内有效。

在同一个块级作用域,不能重复声明。

不存在变量声明,有暂时性死区。

const 除了具有 let 的上述特点外,其还具备一个特点,即 const 定义的变量,一旦定义后,就不能修改,即 const 声明的为常量。const声明的常量必须初始化。

const保存的是地址,地址不可变,但是地址保存的数据可以改变。

5.null 和 undefined 的区别?

首先 UndefinedNull 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefinednull

undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefinednull 主要用于赋值给一些可能会返回对象的变量,作为初始化。

undefined 在 js 中不是一个保留字,这意味着我们可以使用 undefined 来作为一个变量名,这样的做法是非常危险的,它会影响我们对 undefined 值的判断。但是我们可以通过一些方法获得安全的 undefined 值,比如说 void 0

当我们对两种类型使用 typeof 进行判断的时候,Null 类型化会返回 “object”,这是一个历史遗留的问题。当我们使用双等号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false

6.js类型判断

typeof “John” // 返回 string
typeof 3.14 // 返回 number
typeof false // 返回 boolean
typeof [1,2,3,4] // 返回 object
typeof {name:‘John’, age:34} // 返回 object

7.判断一个数组

1.instanceof

使用instanceof来检测某个对象是否是数组的实例,该检测会返回一个布尔型(boolean),如果是数组的话,返回true,否则的话返回false;

2.constructor

var obj = {}; obj.constructor //输出f Object

3.Object.prototypr.toString.call()

4.Array.isArray()

8.this的指向

认绑定:全局环境中,this认绑定到window。

隐式绑定:一般地,被直接对象所包含的函数调用时,也称为方法调用,this隐式绑定到该直接对象。

隐式丢失:隐式丢失是指被隐式绑定的函数丢失绑定对象,从而认绑定到window。

显式绑定:通过call()、apply()、bind()方法把对象绑定到this上,叫做显式绑定。

new绑定:如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。指向实例对象。

9.js同步和异步

异步不按照代码顺序执行,异步的执行效率更高。

10.解决异步回调地狱

promise、generator、async/await

11.promise

Promise 对象有以下两个特点:

1、对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:

  • pending: 初始状态,不是成功或失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。

2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

Promise 优缺点

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

new Promise(function (resolve, reject) {
    var a = 0;
    var b = 1;
    if (b == 0) reject("Divide zero");
    else resolve(a / b);
}).then(function (value) {
    console.log("a / b = " + value);
}).catch(function (err) {
    console.log(err);
}).finally(function () {
    console.log("End");
});

Promise 类有 .then() .catch() 和 .finally() 三个方法,这三个方法的参数都是一个函数,.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,.catch() 则是设定 Promise 的异常处理序列,.finally() 是在 Promise 执行的最后一定会执行的序列。

resolve() 中可以放置一个参数用于向下一个 then 传递一个值,then 中的函数也可以返回一个值传递给 then。但是,如果 then 中返回的是一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作,这一点从刚才的计时器的例子中可以看出来。

reject() 参数中一般会传递一个异常给之后的 catch 函数用于处理异常。

但是请注意以下两点:

  • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列;
  • resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。

Promise.all方法,Promise.race方法

Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

var p = Promise.all([p1,p2,p3]);

上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)

p 的状态由 p1、p2、p3 决定,分成两种情况。

  • (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数
  • (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数

下面是一个具体的例子。

// 生成一个Promise对象的数组 
var promises = [2, 3, 5, 7, 11, 13].map(function(id){  return getJSON("/post/" + id + ".json"); });  Promise.all(promises).then(function(posts) {  // ...   }).catch(function(reason){  // ... });

Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

var p = Promise.race([p1,p2,p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。

如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。

12.await async

异步函数 async function 中可以使用 await 指令,await 指令后必须跟着一个 Promise,异步函数在这个 Promise 运行中暂停,直到其运行结束再继续运行。

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数

async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。

await 关键字仅在 async function 中有效。如果在 async function 函数体外使用 await ,你只会得到一个语法错误

13.具体例子

先同步再异步,异步里面

  • 执行一个宏任务(栈中没有就从事件队列中获取
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取

宏任务:

setTimeout
setInterval

微任务:

Promise.then
async function async1 () {
  console.log('async1 start')
  await async2();
  console.log('async1 end')
}
 
async function async2 () {
  console.log('async2')
}
 
console.log('script start')
 
setTimeout(function () {
  console.log('setTimeout')
}, 0)
 
async1();
 
new Promise (function (resolve) {
  console.log('promise1')
  resolve();
}).then (function () {
  console.log('promise2')
})
 
console.log('script end')

script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout

解析:
先执行主代码块,所以第一行打印script start

setTimeout()是另一个宏任务,所以先放在宏任务队列。

后执行async1,async1里先打印async1 start
执行await async2 , 先执行async2,打印async2, 返回promise对象。await会阻塞async1后面的代码执行,所以先跳出来继续执行后面的代码

后执行new Promise 打印promise1
把then里面的函数加入微任务队列

打印script end
到这里第一个宏任务执行完毕,开始执行微任务then,打印promise2。

then执行完后,await才算是执行结束了,后面的代码不再被阻塞,所以打印async1 end
这时候继续执行第二个宏任务setTimeout,打印setTimeout

11.事件流

HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。

什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。

事件捕获阶段

处于目标阶段

事件冒泡阶段

addEventListener:addEventListener 是DOM2 级事件新增的指定事件处理程序的操作,这个方法接收3个参数:要处理的事件名、作为事件处理程序的函数一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。

IE只支持事件冒泡。

12.事件委托

简介:事件委托指的是,不在事件的发生地(直接dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素DOM的类型,来做出不同的响应。

举例:最经典的就是ul和li标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在li标签上直接添加,而是在ul父元素上添加

好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。

13.闭包

闭包就是一个函数引用另一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会增加内存消耗。

例子:

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
 
add();
add();
add();
 
// 计数器为 3

https://github.com/ZengLingYong/Blog/issues/16

14.原型链

–每个对象拥有一个原型对象(proto),对象以其原型为模板、从原型继承方法属性。原型对象也可能拥有原型对象,并从中继承方法属性,一层一层、以此类推。这种关系常被称为原型链 。

– 层层向上直到一个对象的原型对象为 null 。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

15.原型属性

原型链上查找的属性,也叫继承属性

16.继承

属性的继承只发生在读属性时,而在写属性值时不会发生。

– 读属性时,JavaScript 首先从本对象查找,接着是它的原型,以及原型的原型,直到原型链的顶端。

– 可以访问原型链上的任何一个属性,就好像对象本身拥有该属性一样。

17.作用域链

– 作用域是一套关于如何存储变量当中的值,并且能在之后对这个值进行访问修改的规则

作用域的作用

– 作用域指定变量与函数的可访问范围

– 作用域控制着变量与函数的可见性

作用域链(Scope Chain)

– 由当前执行环境与所有父级执行环境的一系列变量对象组成

– 提供对变量和函数访问的权限和顺序的规则

18.防抖节流

19.callbindapply实现

20.深拷贝浅拷贝,对象里面存着自己用hash

21.实现new

22.跨域

23.箭头函数

24.ES6新增

相关文章

最后的控制台返回空数组.控制台在ids.map函数完成之前运行va...
我正在尝试将rxJava与我已经知道的内容联系起来,特别是来自J...
config.jsconstconfig={base_url_api:"https://douban....
我正在阅读MDN中的javascript,并且遇到了这个谈论承诺并且不...
config.jsconstconfig={base_url_api:"https://douban....
这是我的代码main.cpp:#include<string>#include<...