共计 14070 个字符,预计需要花费 36 分钟才能阅读完成。
前言
上篇博客,咱们理解了 javascript 函数执行过程,本篇章次要讲述函数,如参数、回调、同 / 异步、箭头函数等,并论述了对 ES6 中 class 类的意识以及继承。
面试答复
1.ES6 新个性:新增了一种值类型 Symbol 以及 Set 和 Map 的数据结构。减少了块级作用域,比方 let,const。减少了变量的解构赋值。新增了扩大运算符。提供了类的语法糖 class。新增了箭头函数。函数参数容许设置默认值,引入了 rest 参数,新增了一些 API,比方 isArray、keys()。
2. 异步编程史:异步编程有四个阶段,别离是回调函数、Promise、Generator、async/await,回调函数的毛病在于容易呈现回调天堂,不能用 try/catch 捕捉谬误,不能 return。Promise 构造函数内的代码是同步执行,then 办法中的代码是异步执行的,能够通过构造函数的参数 reslove 进入 then 回调中,不过毛病是无奈勾销,谬误须要回调来捕捉。Generator 能够用来管制函数,比方暂停、复原执行。async/await 是 Promise 的语法糖,它是为优化 then 链而开发进去的,解决了 then 链过长的问题,await 的性能根本等同于 then,它能实现的成果都能用 then 来实现,只不过 then 办法是微工作,属于异步工作,而 await 后的办法等同于 Promise 的构造函数,这个办法之后的代码等同于 then 回调。
3. 箭头函数:箭头函数没有原型,所以不能作为构造函数。它的 this 指向为以后所在环境的 this,且箭头函数不反对重命名参数,也没有 arguments 对象。
4. 闭包:闭包可能使外部函数读取内部函数的变量,让变量长期驻扎在内存当中,从而让该变量不被垃圾回收机制回收,当不再须要闭包时,把外部函数赋值为 null 即可。益处是可能防止因为作用域问题而把变量定义在全局作用域外面,造成全局变量的净化。闭包的原理是利用了函数作用域链的个性,外部函数会将内部函数的流动对象增加到作用域链里,当内部函数执行结束,作用域链会被销毁,但流动对象因为仍被外部函数的作用域链援用,所以不会被销毁。当初闭包很少用到,以前的话会用闭包解决封装模块以及循环中作用域的问题。
5. 手写 Promise:Promise 有状态、构造函数、then 办法、catch 办法、finally 办法、all 等办法,首先定义三个状态,pending、resolved、rejected 以及一些变量,比方 status、data、reason、callback 用来贮存状态、resolve 返回、reject 返回和 then 办法的回调。而后实现构造函数以及 reject、resolved 两个办法,构造函数个别用 try/catch 办法间接接管或抛出,reject、resolved 这两个办法是创立实例的时候作为构造函数的参数传入,且两个办法都蕴含对状态的判断以及批改,并且解决回调。接着实现 then 办法,因为 then 办法是微工作且最初会返回一个 Promise,所以咱们要 new 一个新实例而后判断状态,再执行 queueMicrotask 办法,最初就是一些 catch 办法、finally 等办法的解决,这两个都能够通过间接返回之前实现的 then 办法实现,只不过 finally 不接管参数。Promise 的 all 办法,同样最初会返回一个 Promise,不同的是 Promise.all 办法接管的参数是数组,可能蕴含多个申请,须要用 let…of… 对参数进行遍历,遍历后判断它是否为 Promise 对象,如果是则间接调用 then 办法,如果不是则将参数保留到后果变量中,最初一起返回这个后果变量。
6. 闭包:闭包可能使外部函数读取内部函数的变量,让变量长期驻扎在内存当中,从而让该变量不被垃圾回收机制回收,当不再须要闭包时,把外部函数赋值为 null 即可。益处是可能防止因为作用域问题而把变量定义在全局作用域外面,造成全局变量的净化。闭包的原理是利用了函数作用域链的个性,外部函数会将内部函数的流动对象增加到作用域链里,当内部函数执行结束,作用域链会被销毁,但流动对象因为仍被外部函数的作用域链援用,所以不会被销毁。当初闭包很少用到,比拟典型的就是 vue 中的 data 数据,以前的话会用闭包解决封装模块以及循环中作用域的问题。
知识点
1. 参数
JavaScript 函数有个内置的对象,arguments 对象,argument 对象蕴含了函数调用时的由所有参数组成的数组,arguments 对象能够利用于函数调用时参数太多(超过申明)的情景。
function test() {
let arg = arguments
if(arg.length !== 0) console.log(arg)
}
test(1, 123, 500, 115, 44, 88)
参数默认值
function test(x, y = 10) {return x + y;}
test(0, 2) // 2
test(5) //15
rest 参数(…)
function test(a, ...b) {console.log(a)
console.log(b)
}
test(1, 2, 3, 4, 5)
//1
//[2, 3, 4 ,5]
2.Generator 函数
yield 是 JS 为了解决异步调用的命令。示意程序执行到这里会交出执行权,期待后果返回。它须要在 Generator 函数中运行,与一般 function 的区别就是函数名后面多了一个星号*
,这里只做最简略的用法,有趣味的同学能够持续钻研~
function *generatorForLoop(num) {for (let i = 0; i < num; i += 1) {yield console.log(i);
}
}
const genForLoop = generatorForLoop(5);
genForLoop.next(); // 0
genForLoop.next(); // 1
genForLoop.next(); // 2
genForLoop.next(); // 3
genForLoop.next(); // 4
3. 箭头函数
箭头函数没有原型,自身没有 this,在箭头函数中应用 this,它的指向为以后所在环境的 this,所以不能够当作构造函数,new 一个箭头函数也会抛出一个谬误。箭头函数不反对重命名函数参数,对于箭头函数的 this 指向问题曾经在上一篇博客中了解过了,这边就不着重形容了,咱们分为部分函数环境、全局环境来看。
全局环境
全局环境下,箭头函数的 this 都会指向 window,应用 aguments 会报错,举例:
let foo = ()=>{console.log(this)};
foo(); //window
部分函数环境
依据上一篇 this 指向的了解,在部分函数环境,箭头函数的 this 指向它的外层一般函数时,它的 arguments 指向外层一般函数的 arguments,取而代之用 rest 参数...
。箭头函数自身的 this 指向不能扭转,也不能应用 call、apply 去扭转,但扭转它所在的上下文(也就是外层函数)的 this 指向,举例:
// 状况一:上面的箭头函数 this 的指向是 function b (固定的),而 function b 的 this 指向依据一般函数的 this 指向规定,是指向调用它的对象,也就是 obj,所以箭头函数 --> function b --> obj,也就是箭头函数的 this 指向,指向 obj,obj.a=2
var a = 1
var obj = {
a:2,
b:function(){return ()=>{console.log(this.a)
}
}
}
obj.b()() //2
// 状况二:这时候想要扭转箭头函数 this 指向,能够在箭头函数外包一层匿名函数即可(匿名函数默认指向 window),所以对于箭头函数的 this 指向在创立阶段确定,且无奈扭转的说法是没问题的。var a = 1
var obj = {
a:2,
b:function(){return function(){return ()=>{console.log(this.a)
}
}
}
}
obj.b()()() //1 obj.b()等于 return 的 function,而 obj.b()()等于调用 return 的 function 即 window.b(),所以指向 window
// 状况三:或者间接应用 call 扭转外层函数的 this 指向
var a = 1
var obj = {
a:2,
b:function(){return ()=>{console.log(this.a)
}
}
}
obj.b.call(window)() //1
4.class 类
先来看一下 ES5 的类,
function Animal(word,food){
this.word = word
this.food = food
this.eat = function(){console.log('I eat',this.food)
}
}
再看一下 ES6 残缺的例子,记得有疑难的中央要标记一下哦:
class Animal {
//#privateAnimal = 'Animal Animal' // 参考 A.1. 公有成员
constructor(word, food) { // 参考 A.2. 构造函数
this.word = word
this.food = food
}
static say(word){ // 参考 A.1. 动态成员
console.log(word)
}
eat(){ // 参考 A.1. 私有成员
console.log('I eat',this.food)
}
testSuper(){ // 参考 A.2. 构造函数 super
console.log('super')
}
//#privateCon(){ // 参考 A.1. 公有成员
//console.log(this.#privateCat)
//}
}
class Cat extends Animal { // 参考 A.4. 继承
constructor(word, food) {super() // 参考 A.2. 构造函数 super
this.word = word
this.food = food
}
['play'+'bool'](){ // 参考 A.3. 计算属性名称
console.log('Cat play bool')
}
//get、set 参考 A.3. 拜访器属性
get age(){return 'getter'}
set age(val){console.log('setter',val)
}
getSuper(){ // 参考 A.2. 构造函数 super
console.log(super.testSuper())
}
}
// 参考 A.1. 动态成员
Animal.say('Hello Animal')
Cat.say('Hello Cat')
// 参考 A.1. 公有成员
//Animal.#privateCon() // error, 不可拜访
let animal = new Animal('Hello Animal','meet');
animal.eat()
let cat = new Cat('Hello Cat','fish');
cat.eat()
cat.playbool()
// 参考 A.2. 构造函数 super
cat.getSuper()
//get、set 参考 A.2. 拜访器属性
cat.age = 3
cat.age
A.1. 根底概念
成员:成员分为公有成员、私有成员以及动态成员,无论属性还是函数只有加上关键字都能够成为对应的成员。
公有成员:关键字#
,公有成员有以下几个特点,
- class 外部不同办法间能够应用,因而 this 要指向实例化对象;
- 不能被内部拜访,因而实例化对象既不能取得值,也不能设定值;
- 不能被继承,因而 extends 后子类不具备该属性
动态成员:是指在办法名或属性名后面加上 static
关键字,和一般办法不一样的是,静态方法不能在实例中拜访,只能在类中拜访;动态成员也能够通过派生类拜访,但不能通过派生类的实例拜访,如果静态方法蕴含 this 关键字,这个 this 指的是类,而不是实例。
私有成员:除去公有成员,动态成员,其余成员属于私有成员。
派生类:通过 extends 关键字来实现继承的性能,如 Animal 是 Cat 的基类,Cat 是 Animal 的派生类,Cat 继承了 Animal 的根本能力,在派生类中定义重名函数会笼罩掉基类中的原始函数。
原型链:之前曾经对原型链的概念进行了解,Class 类同时有 prototype
属性和 __proto__
属性,因而同时存在两条继承链。大抵了解如下:
1. 子类实例(cat)的 __proto__
属性,总是指向 cat 原型,而 cat 原型的 __proto__
属性总是指向父类(Animal)的原型,父类实例(animal)的 __proto__
属性则也是指向他本身的原型。也就是说,cat.__proto__.__proto__===animal.__proto__
。子类实例的原型的原型,是父类实例的原型。
2. 子类(Cat)prototype
属性指向的是他本身的原型,而后子类(Cat)原型的原型又是指向父类的原型,也就是说,Cat.prototype.__proto__=== Animal.prototype,
即总是指向父类(Animal)的 prototype
属性。
如若还有疑难,能够参考博客文章四。
A.2. 构造函数
Cat 类中能够看到有一个 constructor
办法,这个就是构造函数。constructor 办法是类的默认办法,通过 new 命令生成对象实例时,主动调用该办法。一个类必须有 constructor 办法,如果没有定义,constructor 办法会被默认增加。
在派生类中,如果应用了构造方法,且用到了 this,就必须应用 super(),且 super()也只能在派生类中应用,在构造函数中拜访之前肯定要调用 super(),它此时代表父类的构造函数,负责 this 的初始化。super 作为对象时,在一般办法中,指向父类的原型对象;在静态方法中,指向父类。
A.3. 函数
拜访器属性:类反对在原型上定义拜访器属性。只管应该在类的构造函数中创立本人的属性,然而类也反对间接在原型上定义拜访器属性。创立 getter 时,须要在关键字 get 后紧跟一个空格和响应的标识符;创立 setter 时,只需把关键字 get 替换为 set 即可。
计算属性名称:类和对象字面量有很多相似之处,类办法和拜访器属性也反对应用可计算名称,用方括号包裹一个表达式,即应用计算名称。
A.4. 继承
extends 关键字后只有是一个有 prototype 属性的函数,就能被继承,至于原型链相干的知识点这里就不赘述了,不分明的同学能够参考博客四,这里顺便聊一下 JS 中常见的继承形式。
原型链继承:父类的实例作为子类的原型,长处是简略易于实现,父类的新增的实例与属性子类都能拜访;毛病是能够在子类中减少实例属性,如果要新减少原型属性和办法须要在 new 父类构造函数的前面,无奈实现多继承,创立子类实例时,不能向父类构造函数中传参数。
function Cat(){}
Cat.prototype= new Animal()
let Cat = new Cat()
结构继承:复制父类的实例属性给子类,长处是解决了子类构造函数向父类构造函数中传递参数,能够实现多继承(call 或者 apply 多个父类);毛病是办法都在构造函数中定义,无奈复用,不能继承原型属性 / 办法,只能继承父类的实例属性和办法。
function Cat(){
// 继承了 Animal
Animal.call(this)
}
let cat = new Cat();
组合继承(原型链 + 构造函数):调用父类构造函数,继承父类的属性,通过将父类实例作为子类原型,实现函数复用。长处是函数能够复用,不存在援用属性问题,能够继承属性和办法,并且能够继承原型的属性和办法;毛病是因为调用了两次父类,所以产生了两份实例。
function Aniaml(food){this.food = food}
Aniaml.prototype.eat = function(){return this.food}
function Cat(food){Aniaml.call(this,food)
}
Cat.prototype = new Aniaml();
Cat.prototype.constructor = Cat;
let cat = new Cat('fish');
cat.eat();
寄生组合继承:通过寄生的形式来修复组合式继承的有余,完满的实现继承
function Animal(food,age){this.food = food || ''}
Animal.prototype.eat = function(){return this.food}
function Cat(food){
// 继承父类属性
Animal.call(food)
}
// 继承父类办法
(function(){
// 创立空类
let super = function(){}
super.prototype = Animal.prototype
// 父类的实例作为子类的原型
Cat.prototype = new super()})();
// 修复构造函数指向问题
Cat.prototype.constructor = Cat
let cat = new Cat()
实例继承:长处是不限度调用形式,简略,易实现;毛病是不能屡次继承。
function Cat(){let animal = new Animal()
return animal
}
let cat = new Cat()
ES6 继承形式:最优
class Animal{constructor(food=''){this.food = food}
eat(){console.log(this.food)
}
}
// 继承父类
class Cat extends Animal{constructor(food = 'meet'){
// 继承父类属性
super(food);
}
eat(){
// 继承父类办法
super.eat()}
}
let cat=new Cat('fish');
cat.eat();
5.Promise
Promise 构造函数内是同步工作,then 办法中属于异步工作的微工作。Promise 的状态有三个值:pending、resolve、rejected,且 Promise 的状态只会产生一次扭转。接下来,咱们先看一下 Promise 的根底用法:
new Promise((reslove,reject)=>{console.log(1)
reslove(2)//reslove 后会进入 then
reject(3)//reject 后会进入 catch, 但 Promise 的状态只会扭转一次, 所以这行不会执行,然而上面那行还是会打印。console.log(4)
}).then(res=>{console.log(res)
}).catch(error=>{console.log(error)
})
Promise.all(): 用于屡次申请,不过如果呈现一个接口申请异样,整个 Promise.all 就会被认定为失败,进入 catch 回调。
//getData1,getData2,getData3 别离为三个接口申请
Promise.all([getData1,getData2,getData3]).then(res=>{const [res1,res2,res3] = res // 用到了解构数组
})
Promise.race(): 与 Promise.all()返回全副数据不同,Promise.race()下面代码中,只有有一个实例率先扭转状态,状态就跟着扭转,返回的为率先扭转的 Promise 实例的返回值。
const a = new Promise((resolve,reject) => {setTimeout(() => {console.log("a")
resolve(1)
},1000)
})
const b = new Promise((resolve,reject) => {setTimeout(() => {console.log("b")
resolve(2)
},2000)
})
Promise.race([a,b]).then(v => {console.log(v)
})
// 输入:a
1
b
1.Promise 执行程序
先看一下常见的 Promise 所对应的面试题来理解执行程序:
console.log(1)
setTimeout(()=>{console.log(6)
},0)
new Promise((reslove,reject)=>{console.log(2)
reslove('zxp')
//reject(2)
}).then(res=>{console.log(3)
console.log(res)
}).then(res=>{console.log(4)
}).catch(err=>{console.log(5)// 没报错,不打印
console.log(err)
})
setTimeout(()=>{console.log(7)
},0)
//1 2 3 zxp 4 6 7
2. 手写 promise
手写 Promise 是高频面试题,特地是在口试题中,同时通过手写 Promise 也有助于咱们进一步了解意识 Promise,参考资料:https://juejin.cn/post/7175516630972104760#heading-6
class myPromise {constructor(func) {
//1. 定义状态以及正确、异样的返回及回调,状态对应 pending、fulfilled、rejected
this.state = 'pending'
this.data = undefined
this.reason = undefined
this.resolveCallbacks = []
this.rejectCallbacks = []
// 2. 实现构造函数,并传入自定义的 resolve、reject
try {func(this.resolve, this.reject)
} catch (e) {this.reject(e)
}
}
//3. 实现 reslove 和 reject 的,两者均是先判断是否为 pending 状态,而后更改为对应状态、赋值返回、执行所有的回调(能够通过 while+ 数组长度 +shift 来实现),不同的是返回和回调不同。resolve = (data) => {
// 一旦状态扭转,就不能再变
if (this.state === 'pending') {
this.state = 'fulfilled'
this.data = data
// 顺次调用胜利回调,直到 successCallback 为空
while (this.resolveCallbacks.length) {// 删除 successCallback 第一个数组元素,并执行这个被删除的元素,并传入参数。(arr.shift()返回值为被删除的元素)this.resolveCallbacks.shift()()
}
}
}
reject = (reason) => {if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
while (this.rejectCallbacks.length) {this.rejectCallbacks.shift()()}
}
}
//4. 实现 then 回调:判断参数是否为函数,返回新的 Promise 来反对链式调用,判断状态后,用 queueMicrotask 办法实现微工作的执行,执行内容为调用传入的 resolve 或 reject,而后调用 resolvePromise 办法。then(onResolve, onRejected) {
// 判断是否为函数,若不是则增加默认函数
onResolve = typeof onResolve === 'function' ? onResolve : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}
// 返回一个新的 promise,来反对链式调用。const newPromise = new myPromise((resolve, reject) => {if (this.state === 'fulfilled') {queueMicrotask(() => {
try {const value = onResolve(this.data)
// 传入的参数别离代表,最新的 Promise,最新的值,最新的 resolve,最新的 reject
this.resolvePromise(newPromise, value, resolve, reject)
} catch (e) {reject(e)
}
})
} else if (this.state === 'rejected') {queueMicrotask(() => {
try {const value = onRejected(this.reason)
this.resolvePromise(newPromise, value, resolve, reject)
} catch (e) {reject(e)
}
})
} else if (this.state === 'pending') {
// 首次进入 then 办法,将回调函数存入数组
this.resolveCallbacks.push(() => {queueMicrotask(() => {
try {const value = onResolve(this.data)
this.resolvePromise(newPromise, value, resolve, reject)
} catch (e) {reject(e)
}
})
})
}
})
return newPromise
}
//5. 实现 Promise 解决程序:为了合乎 Promise A+ 的规定
// 传入的参数别离代表,最新的 Promise,最新的值,最新的 resolve,最新的 reject
resolvePromise = (newPromise, value, resolve, reject) => {
// 这里有三种状况://1. 如果 value 和 promise 是同一个对象,则调用 reject 办法,其参数为 new TypeError()
//2. 如果 value 是对象或函数,且 then 是一个函数,新的 reslove 和 reject 函数作为参数。// 如果 value.then 不是函数,就间接调用 resolve 办法解决 Promise,参数为 value
//3. 如果以上两种情况都没有呈现,调用 resolve 办法解决 Promise,其参数为 value
if (value === newPromise) {
// 循环调用报错
reject(new TypeError())
} else if ((value !== null && typeof value === 'object') || typeof value === 'function') {if (typeof value.then === 'function') {
value.then((newValue) => {resolvePromise(newPromise, newValue, resolve, reject)
},
(r) => {reject(r)
}
)
} else {resolve(value)
}
} else {resolve(value)
}
}
//6. 实现 catch,catch 函数只是没有给 fulfilled 状态预留参数地位的 then 办法
catch (onRejected) {return this.then(undefined, onRejected);
}
//8. 实现 Promise.resolve()办法,如果是 promise 就间接返回 value,否则就 new 一个 promise,默认 resolve 传入 value
static resolve (value) {
return value instanceof myPromise
? value
: new myPromise(resolve => resolve(value));
}
//9. 实现 Promise.reject()办法
static reject (reason) {return new myPromise ((resolve, reject) => {reject(reason);
});
}
//10. 实现 finally,相当于最初再执行 then 办法去调用静态方法 reslove
finally (callback) {
return this.then (
data => {return myPromise.resolve(callback().then (() => data));
},
err => {return myPromise.resolve(callback()).then (() => {throw err;});
}
);
}
//11. 实现 all 办法
static all(promises) {return new myPromise((resolve, reject) => {
let times = 0; // 记录执行次数
let result = []; // 保留执行后果
function addData (key, value) {
// 记录后果
times++;
result[key] = value;
times === promises.length && resolve (result);
}
promises.forEach((element, index) => {if (element instanceof myPromise) {
//promises 数组中的元素是 promise
element.then(value => addData(index,data),
err => reject(err)
)
} else {addData(index,element)
}
})
})
}
}
new myPromise((resolve,reject)=>{console.log(1)
resolve(3)
console.log(2)
}).then(res=>{console.log(res)
}).then(res=>{
debugger
console.log(res)
console.log(4)
})
6.async/await
async/await 其实是 Promise 的语法糖,它是为优化 then 链而开发进去的。它能实现的成果都能用 then 来实现,能够视 await 前面的办法为 Promise 的构造函数,之后的代码等同于 then 回调。
async 的用法,它作为一个关键字放到函数后面,示意函数是一个异步函数,async 函数返回的是一个 promise 对象,能够应用 then 办法增加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作实现,再接着执行函数体内前面的语句。
await 前面应该写一个 Promise 对象,如果不是 Promise 对象,那么会被转成一个立刻 resolve 的 Promise(Promise.resolve())。await 命令前面的 Promise 对象,运行后果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。
样例:
// 函数申明
async function test(){}
// 函数表达式
const res = async function(){}
// 箭头函数
const res = async ()=>{}
次要利用场景:
样例 1:须要期待接口一步步执行
async function getData(){
try{let res1= await getA() //getA 为 Promise,是一个接口申请
if(res1){let res2= await getB() //getB 为 Promise,是一个接口申请
return res2
}
}catch(e){console.log(e)
}
}
样例 2:只须要单个接口期待,其余接口能够异步
async function getData(){getA().then(async (res)=>{
try{
// 须要 res2 数据,能力继续执行
let res2= await getB() //getB 为 Promise,是一个接口申请
console.log(res)
}catch(e){console.log(e)
}
})
}
7. 闭包
在 JavaScript 高级程序设计(第 3 版)中是这样形容闭包:闭包是指有权拜访另一个函数作用域中的变量的函数。
闭包作用:闭包既能够短暂的保留变量又不会造成全局净化。
闭包毛病:占用更多内存;不容易被开释,不会随着函数的完结而主动销毁。
闭包用法:
1、定义外层函数,封装被爱护的局部变量。
2、定义内层函数,执行对外部函数变量的操作。
3、外层函数返回内层函数的对象,并且外层函数被调用,后果保留在一个全局的变量中。
function outTest(){
let a = 1
return function insideTest(){
a++
return a
}
}
outTest()()//outTest()的执行后果返回的是 insideTest 函数,咱们须要再加一个 () 执行 insideTest 函数
// 换个调用形式,后果就不一样了
var func = outTest()
outTest() // 第一次后果都是 2 雷同,而这种调用形式每次 +1,而下面的形式始终不变,这是因为闭包中的变量没有被开释的起因。
8. 递归
简略来说,所谓的递归函数就是在函数体内调用 n 次本函数,直到达到某个条件从而进行。
递归利用的场景须要具备以下三种因素:
1、存在递归终止条件:递归进口
2、一个问题能够合成为更小的问题用同样的办法解决:递归表达式(法则)
3、合成后的子问题求解形式一样,不同的是数据规模变小
// 斐波那切数列:本身等于前两项之和,即 n = (n-1) +(n-2)
//1、1、2、3、5、8、13
function fibonacci(n){if(n<=2){return 1}
return fibonacci(n-1)+fibonacci(n-2)
}
console.log(fibonacci(6))
9. 高阶函数
高阶函数是指至多满足下列条件之一的函数:
1、函数能够作为参数被传递
2、函数能够作为返回值输入。
所以一个函数就能够接管另一个函数作为参数,这种函数就称之为高阶函数。
最罕用的高阶函数,即咱们日常接口所用的回调或者是对数据的应用,比方 map、reduce、filter、sort 或如下:
// 假如 getData 是一个接口申请
getData().then(res=>{console.log(res)
})
getData().then(function(res){console.log(res)
})
const arr = [1,2,3]
const arr2 = arr.map(item=>item*2)
const arr3 = arr.map(function(item){return item+10})
最初
走过路过,不要错过,点赞、珍藏、评论三连~