这一次,彻底搞懂箭头函数

Posted Always--Learning

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了这一次,彻底搞懂箭头函数相关的知识,希望对你有一定的参考价值。

一、箭头函数的特点

1. 相比普通函数,箭头函数有更加简洁的语法。

普通函数

function add(num) 
  return num + 10

箭头函数

const add = num => num + 10;

2. 箭头函数不绑定this,会捕获其所在上下文的this,作为自己的this。

这句话需要注意的是,箭头函数的外层如果有普通函数,那么箭头函数的this就是这个外层的普通函数的this,箭头函数的外层如果没有普通函数,那么箭头函数的this就是全局变量。

下面这个例子是箭头函数的外层有普通函数。

let obj = 
  fn:function()
      console.log('我是普通函数',this === obj)   // true
      return ()=>
          console.log('我是箭头函数',this === obj) // true
      
  

console.log(obj.fn()())

下面这个例子是箭头函数的外层没有普通函数。

let obj = 
    fn:()=>
        console.log(this === window);
    

console.log(obj.fn())
// true

3. 箭头函数是匿名函数,不能作为构造函数,不可以使用new命令,否则后抛出错误。

4. 箭头函数不绑定arguments,取而代之用rest参数解决,同时没有super和new.target。

箭头函数没有arguments、super、new.target的绑定,这些值由外围最近一层非箭头函数决定。

下面的这个函数会报错,在浏览器环境下。

let f = ()=>console.log(arguments);

//报错
f(); // arguments is not defined

下面的箭头函数不会报错,因为arguments是外围函数的。

function fn()
  let f = ()=> 
    console.log(arguments)
  
  f();

fn(1,2,3) // [1,2,3]

箭头函数可以通过拓展运算符获取传入的参数。

5. 使用call,apply,bind并不会改变箭头函数中的this指向。

  • 当对箭头函数使用call或apply方法时,只会传入参数并调用函数,并不会改变箭头函数中this的指向。
  • 当对箭头函数使用bind方法时,只会返回一个预设参数的新函数,并不会改变这个新函数的this指向。

请看下面的代码

window.name = "window_name";

let f1 = function () 
return this.name;
;
let f2 = () => this.name;

let obj =  name: "obj_name" ;

console.log(f1.call(obj));  //obj_name
console.log(f2.call(obj));  // window_name
console.log(f1.apply(obj)); // obj_name
console.log(f2.apply(obj)); // window_name
console.log(f1.bind(obj)());  // obj_name
console.log(f2.bind(obj)());  // window_name

6. 箭头函数没有原型对象prototype这个属性

由于不可以通过new关键字调用,所以没有构建原型的需求,所以箭头函数没有prototype这个属性。

let F = ()=>;
console.log(F.prototype) // undefined

7. 不能使用yield关键字,不能用作Generator函数

二、arguments辨析

既然上文我们提到了arguments,那么下面我们就仔细讲讲这个arguments。

arguments有什么用?

arguments对象是所有非箭头函数中都可用的局部变量,可以使用arguments对象在函数中引用函数的参数,此对象包含传递给函数的每一个参数,第一个参数在索引0的位置。

如何将arguments对象转换为数组

  1. 通过slice
  2. 通过拓展运算符
  3. 通过Array.from
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);

const args = Array.from(arguments);
const args = [...arguments];

arguments函数如何调用自身函数?

我们先看看下面这个函数,这个是可以正常运行的。

function factorial (n) 
    return !(n > 1) ? 1 : factorial(n - 1) * n;


[1,2,3,4,5].map(factorial);

但是作为匿名函数则不行。

[1,2,3,4,5].map(function (n) 
    return !(n > 1) ? 1 : /* what goes here? */ (n - 1) * n;
);

因此arguments.callee诞生了。

[1,2,3,4,5].map(function (n) 
    return !(n > 1) ? 1 : arguments.callee(n - 1) * n;
);

所以arguments要想调用自身的匿名函数,可以通过arguments.callee来调用。

#yyds干货盘点#这一次,彻底搞懂Promise


theme: healer-readable
highlight: obsidian


这是我参与2022首次zz​一、为什么要引入Promise?

在介绍本章之前,首先先抛出几个问题:

  • Promise解决了什么问题?
  • Promise有哪些具体的使用场景?

Promise解决了什么问题?

  1. 回调地狱问题

在没有Promise之前,前端获取数据往往需要通过回调函数层层嵌套的方式来解决异步问题,例如下面这段代码实例:

// 回调地狱实例

// 奶茶函数
function getTea(fn)
setTimeout(() =>
fn(获取到一杯奶茶)
,2000)


// 面包函数
function getBread(fn)
setTimeout(() =>
fn(获取到一个面包)
,100)


// 如果必须按照顺序获取,而不是根据时间,要求是先获取到奶茶后获取到面包。
getTea(function(data)
console.log(data);
getBread(function(data)
console.log(data);
)
)
  1. 可读性问题

通过Promise我们可以将上面的代码重写为下面的方式,明显这样可读性更高。

// 下面解释下,如何通过Promise来解决回调地狱的问题
function getTea()
return new Promise((resolve) =>
setTimeout(() =>
resolve(获取到一杯奶茶)
, 2000)
)


function getBread()
return new Promise((resolve) =>
setTimeout(() =>
resolve(获取到一个面包)
, 500)
)


getTea()
.then(res =>
console.log(res);
return getBread();
)
.then(res =>
console.log(res);
)
  1. 信任问题(也叫回调多次执行问题)

传统的回调函数无法保证只被执行一次,回调函数还要可能被执行其他操作,而Promise调用且仅调用一次resolve,不会产生回调多次执行的问题,所以Promise很好的解决了第三方库多次调用回调的问题。

Promise有哪些具体的使用场景?

  • 场景1:将图片的加载写成一个Promise,图片一旦加载完成,Promise的状态就会发生变化。
  • 场景2:当下一个异步请求需要依赖上一个请求结果的时候,可以通过链式操作解决问题。
  • 场景3:通过all()实现多个请求合并在一起,汇总所有的请求结果,只需设置一个loading即可。
  • 场景4:通过race()可以设置图片请求超时。

二、手写Prromise身上的方法

手写Promise.all

Promise.all的特点是接收的是一个可迭代对象,当这个可迭代对象中的所有元素都执行成功会返回一个数组,一个出错则立即返回错误。

function myPromiseAll(iterable) 
// 首先明确要返回的对象是一个Promise
return new Promise((resolve,reject) =>
// 首先将可迭代对象转换为数组
const promises = Array.from(iterable);
let flag = 0;
const result = [];
// 开始遍历执行
for (let i = 0; i < promises.length; i++)
Promise.resolve(promises[i]).then(res =>
result[i] = res;
flag++;
if (flag === promises.length)
resolve(result)

).catch(err =>
reject(err)
)

)

手写Promise.race

Promise.race函数接收的是一个可迭代对象,相当于让这个可迭代对象中的所有promise对象进行赛跑,只要有一个promise对象发生了状态变化,那么直接返回这个promise对象返回的结果。

// 手写promise.race
function myPromiseRace(iterator)
// 首先返回的是一个promise对象
return new Promise((resolve,reject) =>
for (let item of iterator)
Promise.resolve(item).then(res =>
resolve(item);
).catch(err =>
reject(err);
)

)


let p1 = new Promise(resolve =>
setTimeout(resolve, 105, p1 done)
)
let p2 = new Promise(resolve =>
setTimeout(resolve, 100, p2 done)
)
myPromiseRace([p1, p2]).then(data =>
console.log(data); // p2 done
)

手写Promise.finally

Promise.finally的特点

  • 无论成功还是失败,都会执行这个方法
  • 返回的是一个Promise

Promise.finally执行的例子

let p = new Promise((resolve,reject) => 
setTimeout(() =>
resolve(111);
,2000)
)

p.then(res =>
console.log(res); // 111
).finally(() =>
console.log(无论如何这里都会被执行); // 无论如何这里都会被执行
)

手写Promise.finally(Promise.finally返回的本质上是一个then方法,需要在then方法中执行我们传入的参数,然后返回形参)

Promise.prototype.finally = function(f) 
return this.then((value) =>
return Promise.resolve(f()).then(() => value)
,(err) =>
return Promise.resolve(f()).then(() =>
throw err;
)
)

Promise.all和Promise.race的区别

Promise.all()成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候返回的是最先被reject的值。当Promise.all()的结果是成功的时候,返回结果的数组里边的数据顺序和Promise.all()接收到的promise顺序是一致的。

promise.race表示多个Promise赛跑的意思,里面哪个结果执行的快就返回哪个结果,不管结果本身是成功还是失败,其他Promise代码还会执行,只是不会返回。

Promise.all和Promise.race的应用场景

promise.all()的应用场景

  • 多个异步任务都得到结果时,进行显示的场景

比如,当用户点击按钮时,会弹出一个对话框,这个对话框中的数据来自两个不同的后端接口获取的数据,当用户刚点击的时候,显示的时数据加载中的状态,当这两部分数据都从接口获取到数据的时候,才让数据加载中的状态消失,此时就可以使用Promise.all方法。

Promise.race()的应用场景

  • 提示用户请求超时

比如,当用户点击按钮发送请求的时候,当后端的接口超过我们设定的时间还没有获取到数据的时候,我们就可以提示用户请求超时。

三、Promise是如何解决串行和并行的?

什么是并行?什么是串行?

并行:指的是多个异步请求同时进行。

串行:一个异步请求完成之后再进行下一个请求。

Promise实现并行请求

Promise实现并行请求主要是依靠Promise.all方法和Promise.race方法,我们可以通过手写Promise.all方法或Promise.race方法来实现这一目标。

Promise实现串行请求

Promise实现串行请求主要是借助reduce函数。可以参考我的这篇文章如何控制Promise的串行执行?

// 借助reduce函数来实现Promise的串行执行
const funcArr = [
() =>
return new Promise((resolve) =>
setTimeout(() => resolve(1),2000)
)
,
() =>
return new Promise((resolve) =>
setTimeout(() => resolve(2),1000)
)
,
() =>
return new Promise((resolve) =>
setTimeout(() => resolve(3),3000)
)
,
];

function inOrder(arr)
const res = [];
return<

以上是关于这一次,彻底搞懂箭头函数的主要内容,如果未能解决你的问题,请参考以下文章

这一次带你彻底搞懂JS继承

彻底搞懂 JavaScript 中的 this 指向

9 个优秀的 VUE 开源项目,这一次让你彻底搞懂 VUE

实践这一次,彻底搞懂浏览器缓存机制

这次彻底搞懂JavaScript中的原型与原型链

这一次,彻底搞懂Java中的synchronized关键字