20

1-js 是单线程还是多线程?

单线程,单位时间内只能处理一个进程

2-let 和 var 声明变量的区别?

声明后未赋值,表现相同
'use strict';

(function() {
var varTest;
let letTest;
console.log(varTest); //输出undefined
console.log(letTest); //输出undefined
}());

使用未声明的变量,表现不同:
(function() {
console.log(varTest); //输出undefined(注意要注释掉下面一行才能运行)
console.log(letTest); //直接报错:ReferenceError: letTest is not defined

var varTest = 'test var OK.';
let letTest = 'test let OK.';
}());

重复声明同一个变量时,表现不同:
'use strict';

(function() {
var varTest = 'test var OK.';
let letTest = 'test let OK.';

var varTest = 'varTest changed.';
let letTest = 'letTest changed.'; //直接报错:SyntaxError: Identifier 'letTest' has already been declared

console.log(varTest); //输出varTest changed.(注意要注释掉上面letTest变量的重复声明才能运行)
console.log(letTest);
}());

变量作用范围,表现不同
'use strict';

(function() {
var varTest = 'test var OK.';
let letTest = 'test let OK.';

{
var varTest = 'varTest changed.';
let letTest = 'letTest changed.';
}

console.log(varTest); //输出"varTest changed.",内部"{}"中声明的varTest变量覆盖外部的letTest声明
console.log(letTest); //输出"test let OK.",内部"{}"中声明的letTest和外部的letTest不是同一个变量
}());

3-说说 async/await 的使用方式和场景

   假设我们有如下两个方法:
复制代码
  function convertToBase64Data(url) { // 转成base64格式
    return new Promise( resolve => {
      setTimeout( () => {
        resolve('img');
      }, 1000);
    });
  }
  function saveToLocal(img) { // 保存到本地
    return new Promise( resolve=> {
      setTimeout( () => {
        resolve('success');
      }, 200);
    });
  }
复制代码

场景1:多个异步需要按顺序:图片处理完成然后保存在本地

用promise我们可能这样写:

    function fn1() {
        return convertToBase64Data('http://1.jpg').then( base64Data => {
            return saveToLocal(base64Data);
        })
    }

使用await则更简洁,更好理解:

  async function fn1() {
    const base64Data = await download('http://1.jpg');
    const result = await saveToLocal(base64Data); 
    return result;
  }

场景2:图片需要同时处理,但是要按顺序一张一张保存在本地

不使用await:

复制代码
    function fn2() {
       const promise1 = convertToBase64Data('http://1.jpg');
       const promise2 = convertToBase64Data('http://1.jpg');
       Promise.all([promise1,promise2]).then( datas => {
           saveToLocal(datas[0]).then( result1 => {
               saveToLocal(datas[1]).then(result2 => {
                   console.log(result2);
               })
           })
       })
    }
复制代码

我们看到,回调很多,很不方便,下面是使用await:

复制代码
    async function fn2() {
        // 同时处理两张
        const promise1 = convertToBase64Data('http://1.jpg');
        const promise2 = convertToBase64Data('http://2.jpg');
        const [data1, data2] = await Promise.all([promise1, promise2]);
        // 先保存第一张
        const result = await saveToLocal(data1);
        // 再保存第二张
        const result2 = await saveToLocal(data2);
    }
复制代码

代码层次很清晰,很容易阅读。

场景3:多张图片,处理一张保存一张,然后才能处理下一张

不使用await:

 // 你来写写看!!!

使用await:

复制代码
    async function fn3() {
        const imgUrls = ['http://1.jpg', 'http://2.jpg', 'http://3.jpg', 'http://4.jpg', 'http://5.jpg'];
        for (let i = 0; i < imgUrls.length; i++) {
          const base64Data = await convertToBase64Data(imgUrls[i]);
          const result = await saveToLocal(base64Data);
          console.log(result);
        }
    }
复制代码

场景4:条件语句

不使用await:

复制代码
  function fn4(needConvertToBase64) {
    return download('http://1.jpg').then( img => {
      if (needConvertToBase64) {
        return convertToBase64(img).then(base64Data => {
          return base64Data;
        });
      } else {
        return img;
      }
    });
  }
复制代码

return 到让人迷茫。下面使用await

复制代码
  async function fn4(needConvertToBase64) {
    const img = await download('http://1.jpg');
    if (needConvertToBase64) {
      const base64Data = await convertToBase64(img);
      return base64Data;
    }
    return img;
  }
复制代码

场景5: 你可能会这样:调用方法1,使用方法1返回的结果去调用方法2,然后使用两者的结果去调用方法3。

假设有如下业务: 获取用户ID,然后根据ID获取用户信息,然后将两者保存在服务器。

复制代码
  function getUserId() {
    return Promise.resolve('123123');
  }
  function getUserInfo(id) {
    return Promise.resolve({name: 'aaron'});
  }
  function saveToServer(id, userInfo) {
    return Promise.resolve('success');
  }
复制代码

你的代码很可能是这样的:

复制代码
 function fn5() {
    return getUserId().then( id => { // 拿到id
      return getUserInfo(id).then( userInfo => {
        return saveToServer(id, userInfo);
      });
    });
  }
复制代码

使用await:

 async function fn5() {// 使用await
    const id = await getUserId();
    const userInfo = await getUserInfo(id);
    return saveToServer(id, userInfo);
  }

场景6:错误处理

不使用await,try/catch不能捕获saveToLocal的错误,convertToBase64 的Promise中,只能.catch处理,这样错误处理代码非常冗余,使代码很复杂:

复制代码
  function fn6() {
    try {
      convertToBase64('http://1.jpg').then( data => {
        // saveToLocal可能会出错
        saveToLocal(data).then( result => {
          console.log(result);
        });
        // .catch(err => { console.log(err)}); // 只能在.catch中处理
      });
    } catch (err) { // 这里取不到saveToLocal的错误
      console.log(err);
    }
  }
复制代码

使用await,try/catch能捕获saveToLocal的错误:

复制代码
  async function fn6() {
    try {
      const data = await convertToBase64('http://1.jpg');
      const result = await saveToLocal(data);
      console.log(result);
    } catch (err) {
      console.log(err);
    }
  }
复制代码

4-谈谈对 promise 的理解

            1.代码结构更加扁平且更可读,清晰明了。

            2.能解决回调地狱问题。

            3.可将数据请求和业务逻辑分离开来。

            4.便于管理维护。

            5.能更好的捕获错误。

     6.Promise 是一个构造函数,对回调函数的一种封装,对异步编程的一种改进,用同步的方式表达出来。

5-箭头函数有什么作用和实际应用场景?

  • 箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下,例如:用在 map、reduce、filter 的回调函数定义中
  • 箭头函数的亮点是简洁,但在有多层函数嵌套的情况下,箭头函数反而影响了函数的作用范围的识别度,这种情况不建议使用箭头函数
  • 箭头函数要实现类似纯函数的效果,必须剔除外部状态。所以箭头函数不具备普通函数里常见的 this、arguments 等,当然也就不能用 call()、apply()、bind() 去改变 this 的指向
  • 箭头函数不适合定义对象的方法(对象字面量方法、对象原型方法、构造器方法),因为箭头函数没有自己的 this,其内部的 this 指向的是外层作用域的 this

    const json = {
        bar: 1,
        fn: () => console.log(this.bar)
    };
    
    json.fn();  //-> undefined
    // this 并不是指向 json 这个对象,而是再往上到达全局作用域
    function Foo() {
        this.bar = 1;
    }
    Foo.prototype.fn = () => console.log(this.foo);
    
    const foo = new Foo();
    foo.fn();  //-> undefined
    // this 并不是指向 Foo,根据变量查找规则,回溯到了全局作用域
    const Message = (text) => {  
        this.text = text;
    };
    var helloMessage = new Message('Hello World!');  
    console.log(helloMessage.text); //-> Message is not a constructor
    // 不可以当作构造函数,也就是说,不可以使用 new 命令
  • 箭头函数不适合定义结合动态上下文的回调函数(事件绑定函数),因为箭头函数在声明的时候会绑定静态上下文

    const button = document.querySelector('button');
    button.addEventListener('click', () => {  
        this.textContent = 'Loading...';
    });
    // this 并不是指向预期的 button 元素,而是 window
posted @ 2021-03-07 23:00  故人-w  阅读(28)  评论(0)    收藏  举报