当前位置:   article > 正文

面试题汇总 --js 进阶_js进阶面试题

js进阶面试题

目录

1、实现call、apply及bind函数

call方法的实现:

apply方法的实现 

bind方法的实现

2、解释0.1+0.2!=0.3

3、new的原理是什么? 通过字面量和new创建对象的区别?

4、instanceof的原理及实现

5、Promise的特征是什么? Promise构造函数执行和then函数执行的区别?

6、实现一个简单的Promise

7.async及await的特点,await原理是什么?

 async 对异常错误的处理

8.垃圾回收机制是怎样的?


1、实现call、apply及bind函数

    对于call,apply的理解:

1、call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。

2、apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。

3、bind 第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。

4、bind返回对应函数, 便于稍后调用; apply, call则是立即调用

call方法的实现:

  1. /**
  2. * 实现思路:
  3. * 1. 当不传入第一个参数时,则默认上下文环境为window
  4. * 2. 改变this 指向,让新的对象可以执行该函数,并能接受参数
  5. *
  6. */
  7. Function.prototype.myCall=function(context){
  8. //如果调用者不是函数则抛出异常
  9. if(typeof this!=='function'){
  10. throw new TypeError('Error');
  11. }
  12. //如果context,没有传,或者传入undefined null 则this 执行 window
  13. context=context || window;
  14. context.fn=this;
  15. const args=[...arguments].slice(1);
  16. const result=context.fn(...args);
  17. delete context.fn;
  18. return result;
  19. }

apply方法的实现 

  1. /**
  2. * apply实现思路与call相同,只是参数处理方式不同
  3. */
  4. Function.prototype.myApply=function(context){
  5. if(typeof this !=='function'){
  6. throw new TypeError('Error');
  7. }
  8. context=context||window;
  9. context.fn=this;
  10. let result=null;
  11. //如果传入参数则出入
  12. if(arguments[1]){
  13. result=context.fn(...arguments[1]);
  14. }else{
  15. result=context.fn();
  16. }
  17. //释放内存空间
  18. delete context.fn;
  19. return result;
  20. }

bind方法的实现

  1. /**
  2. * 实现思路如下:
  3. * 1. 对传入context的处理,如果不传或传null、undefined 则赋值为window
  4. * 2. 对于返回函数调用形式处理:
  5. * 2.1 普通函数调用
  6. * 对于该形式我们应该处理 f.bind(obj,2)(4)形式参数的处理
  7. * 2.2 new的方式调用
  8. * 对于该形式来说,this不会被外界传入改变
  9. */
  10. Function.prototype.myBind=function(context){
  11. if(typeof this !=='function'){
  12. throw new TypeError('error');
  13. }
  14. const that=this;
  15. const args=[...arguments].slice(1);
  16. context=context||window;
  17. return function F(){
  18. if(this instanceof F){
  19. return new that(...args,...arguments);
  20. }
  21. return that.apply(context,args.concat(...arguments));
  22. }
  23. }

2、解释0.1+0.2!=0.3

JS浮点数计算精度问题是因为某些小数没法用二进制精确表示出来。JS使用的是IEEE 754双精度浮点规则。

  1. /**
  2. * 因为 0.1 和0.2 在表示小数时是无限循的
  3. * IEEE 754双精度版本(64)位就将超出部分进行裁剪
  4. * 所以就会出现下面的情况
  5. */
  6. //true
  7. 0.100000000000000002===0.1
  8. // true
  9. 0.200000000000000002 === 0.2
  10. parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true

 

修正方案:

  • 使用特殊的进制数据类型,如前文提到的bignumber(对精度要求很高,可借助这些相关的类库)
  • 通过toFixed来进行修正后再转回数字(parseFloat)

3、new的原理是什么? 通过字面量和new创建对象的区别?

  1. 创建一个空对象,作为将要返回的对象实例
  2. 将这个空对象的原型,指向构造函数的prototype属性
  3. 绑定函数内部的this
  4. 开始执行构造函数
  1. function create(){
  2. //设置一个空对象
  3. let obj={};
  4. //取出构造函数
  5. var Cons=[].shift.call(arguments);
  6. //让空对象继承构造函数的prototype
  7. obj.__proto__=Cons.prototype;
  8. //执行构造函数
  9. let result=Cons.apply(obj,arguments);
  10. //确保返回的是一个对象
  11. return result instanceof Object?result:obj;
  12. }
  13. //测试如下:
  14. create(Person,'cc');
  15. function Person(name){
  16. this.name=name;
  17. console.log(name);
  18. }

2. 如果通过字面量创建对象,不需要通过作用域链一层层找到Object. 

4、instanceof的原理及实现

instanceof 可以检测某个对象是不是另一个对象的实例。

  1. /**
  2. *用来判断 用来判断 left对象是不是right对象的实例
  3. */
  4. function _instanceof(left,right){
  5. let prototype=right.prototype;
  6. left=left.__proto__;
  7. while(true){
  8. if(left===null || left===undefined){
  9. return false;
  10. }
  11. if(prototype===left){
  12. return true;
  13. }else{
  14. left=left.__proto__;
  15. }
  16. }
  17. }

 原型继承的原理图:

 该图应该注意以下几点:

      1. 一切函数都是由 Function函数所创造

      2. 一切函数的原型都指向Object, Object原型的原型指向 null

**:instanceof 操作符用来比较两个操作数的构造函数。只有在比较自定义的对象时才有意义

5、Promise的特征是什么? Promise构造函数执行和then函数执行的区别?

Promise特征: 

  • 链式调用
  • 状态不可逆

Promise通过链式调用的方式解决回调嵌套问题,使我们代码更容易理解和维护;Promise 有三种状态 pending,resolved,rejected 这个承诺状态一旦从等待状态变成了其它状态就不能更改状态

Promise构造函数内的代码会立即

  1. console.log('外层执行环境');
  2. let p=new Promise((resolve,reject)=>{
  3. console.log('promise 执行');
  4. resolve('1');
  5. console.log('resolve 后面的语句');
  6. });
  7. p.then(resolveA)
  8. .then((res)=>console.log(res));
  9. function resolveA (value){
  10. return value+' success';
  11. }
  12. /**
  13. *
  14. * 外层执行环境
  15. * promise 执行
  16. * resolve 后面的语句
  17. * 1 success
  18. */

6、实现一个简单的Promise

  1. const PENDING='pending';
  2. const RESOLVED='resolved';
  3. const REJECTED='rejected';
  4. function MyPromise(fn){
  5. //绑定this 让 下面函数中回调不丢失
  6. const that=this;
  7. that.state=PENDING;
  8. that.value=null;
  9. that.resolvedCallBacks=[];
  10. that.rejectedCallBacks=[];
  11. function resolve(value){
  12. if(that.state===PENDING){
  13. that.state=RESOLVED;
  14. that.value=value;
  15. //执行 then中的方法
  16. that.resolvedCallBacks.map(call=>call(that.value))
  17. }
  18. }
  19. function reject(value){
  20. if(that.state===PENDING){
  21. that.state=REJECTED;
  22. that.value=value;
  23. that.rejectedCallBacks.map(call=>call(that.value))
  24. }
  25. }
  26. //执行promise 中的函数
  27. try {
  28. console.log('new Promise');
  29. fn(resolve,reject);
  30. } catch (error) {
  31. reject(error);
  32. }
  33. }
  34. MyPromise.prototype.then=function(onFulfilled,onRejected){
  35. console.log('执行 then方法');
  36. const that=this;
  37. onFulfilled=typeof onFulfilled==='function'?onFulfilled:f=>f;
  38. onRejected=typeof onRejected==='function'?onRejected:error=>{throw error}
  39. if(that.state===PENDING){
  40. console.log('将 then中方法放到 数组中');
  41. that.resolvedCallBacks.push(onFulfilled);
  42. that.rejectedCallBacks.push(onRejected);
  43. }
  44. //将then中的方法放到数组中
  45. if(that.state===RESOLVED){
  46. onFulfilled(that.value);
  47. }
  48. if(that.state===REJECTED){
  49. onRejected(that.value);
  50. }
  51. }
  52. /**
  53. * 测试如下:
  54. */
  55. console.log('外层执行环境');
  56. let p=new MyPromise((resolve,reject)=>{
  57. console.log('promise 回调函数执行');
  58. setTimeout(()=>{resolve('1')},0);
  59. console.log('resolve 后面的语句');
  60. });
  61. p.then((res)=>{console.log(res+' success')});
  62. //测试结果
  63. /**
  64. 外层执行环境
  65. new Promise
  66. promise 回调函数执行
  67. resolve 后面的语句
  68. 执行 then方法
  69. 将 then中方法放到 数组中
  70. 1 success
  71. */

**: 可以通过我打印的日志把函数的执行顺序走一步.

 

7.async及await的特点,await原理是什么?

async函数是Generator函数的语法糖,在函数内部使用 await来表示异步。

  • async 函数返回一个Promise对象
  • async 函数返回的Promise对象,必须等到内部所以 await命令Promise对象执行完,才会发生状态改变
  • async函数中若await 改变了状态,则后面的await都不会被执行
  1. /**
  2. * async 函数返回一个Promise对象
  3. * 函数内部 return 返回的值,会成为then方法回调的参数
  4. * 函数内部异常,会被 catch方法接收到。
  5. */
  6. async function test(){
  7. return '1';
  8. }
  9. async function test1(){
  10. throw new Error('error');
  11. }
  12. test()
  13. .then(v=>console.log(v))
  14. .catch(e=>console.log(e));
  15. test1()
  16. .then(v=>console.log(v))
  17. .catch(e=>console.log(e));
  1. /**
  2. *
  3. * 当 await 返回的是Promise对象时,
  4. * 只有等到所有 await中 promise执行完后,才执行then回调
  5. */
  6. const delay=t=>new Promise(resolve=>setTimeout(resolve,t));
  7. async function f(){
  8. await delay(1000);
  9. await delay(2000);
  10. return 'done';
  11. }
  12. f().then(v=>console.log(v)); //3s 后执行打印
  13. console.log('我先被执行');
  1. /**
  2. * 当 await后跟的是有状态的Promise时,
  3. * 此时之后所有的await都不会被执行
  4. */
  5. let a;
  6. async function func() {
  7. await Promise.reject('error');
  8. a = await 1;
  9. }
  10. func()
  11. .then(v => console.log(v, '-----------', a))
  12. .catch(e => console.log(e));
  13. //输出 error,而 then方法不会被执行

 async 对异常错误的处理

  1. /**
  2. * 通过 try-catch 对异常捕获
  3. * 使得后面的 await被执行
  4. */
  5. async function funcTryCatch() {
  6. try {
  7. await Promise.reject('error');
  8. } catch (error) {
  9. console.log('error,from try-cath')
  10. }
  11. a = await 1;
  12. return a;
  13. }
  14. funcTryCatch()
  15. .then(v => console.log(v, '-----------', a))
  16. .catch(e => console.log(e));

8.垃圾回收机制是怎样的?

V8实现了准确式GC,GC算法采用分代式垃圾回收机制。因此V8内存堆分为新生dui代和老生代两部分。

新生代算法

新生代中的对象一般存活时间较短,使用 Scavenge GC 算法。

在新生代空间中,内存空间分为两部分,分别为 From 空间和 To 空间。在这两个空间中,必定有一个空间是使用的,另一个空间是空闲的。新分配的对象会被放入 From 空间中,当 From 空间被占满时,新生代 GC 就会启动了。算法会检查 From 空间中存活的对象并复制到 To 空间中,如果有失活的对象就会销毁。当复制完成后将 From 空间和 To 空间互换,这样 GC 就结束了。

老生代算法

老生代中的对象一般存活时间较长且数量也多,使用了两个算法,分别是标记清除算法和标记压缩算法。

 

该文内容,会不断添加... 

参考原文:https://juejin.im/post/596e142d5188254b532ce2da

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/555086
推荐阅读
相关标签
  

闽ICP备14008679号