函数
默认参数的设置
//在传参处定义默认值:ES6
const createBooking = function(Num,
passengers = 1,
price = 199 * passengers){
//ES5:
passengers = passengers || 1;//利用短路特性
price = price || 199;
}
函数优先
将函数看作对象(Object)的一种,函数也是一种值,因此可以将他们存储为一个变量const a = function(){}
,或将一个函数作为返回值返回,还有函数的方法(methods)
高阶函数:higher order function
两种形式:
- 接收另一个函数作为参数的函数:
如addEventListener:btnNew.addEventListener('click', fA() {} );
;
fA()为回调函数:当监听器被调用,则调用fA; - 返回新函数
回调函数例:
优势:使函数更加抽象,避免代码复用
'use strict';//严格模式,语法检测会更严格,错误少
const oneWord = function (str) {
return str.replace(/ /g, '').toLowerCase();
};
const upperFirstword = function (str) {
const [first, ...rest] = str.split(' ');
return [first.toUpperCase(), ...rest].join(' ');
};
const transformer = function (str, fn) { //fn为需要传入的函数
console.log(`Original string: ${str}`);
console.log(`Transformed string: ${fn(str)}`);
console.log(`Transformed by: ${fn.name}`);
}
//不同的调用
transformer('JavaScript is the best!', upperFirstword);
transformer('JavaScript is the best!', oneWord);
函数返回函数
const greet = function (greeting) {
return function (name) {
console.log(`${greeting} ${name}`);
}
};
const greeterHey = greet('Hey');
greeterHey('Jonas');
greeterHey('Steven');
//另一种调用方式
greet('Hello')('Jonas');
//箭头函数方式编写
const greetArr = greeting => name =>console.log(`${greeting} ${name}`);
call、apply、bind
call函数
call是函数的函数,参数首先是函数中this的指向,然后是其他参数:
call函数可以操纵this
//对象1
const lufthansa = {
airline: 'Lufthansa',
iataCode: 'LH',
bookings: [],
// book: function() {}
book(flightNum, name) {
console.log(
`${name} booked a seat on ${this.airline} flight ${this.iataCode}${flightNum}`
);
this.bookings.push({ flight: `${this.iataCode}${flightNum}`, name });
},
};
//对象2
const eurowings = {
airline: 'Eurowings',
iataCode: 'EW',
bookings: [],
};
//将函数提取出来
const book = lufthansa.book;
//对新函数使用call,函数中有this,首先指定this的指向
book.call(eurowings, 23, 'Sarah Williams');
apply函数
- 与call类似,但是后面须传入一个数组:
Function.apply(obj,args)
- 可被call代替,使用展开运算符使数组展开即可
bind函数
bind使this绑定在对象上:
const bookEW = book.bind(eurowings);
//this在调用时无需指定
bookEW(23, 'Steven Williams');
//可设置预设参数:
const bookEW23 = book.bind(eurowings, 23);//23是预设参数
this会随着上下文的改变而改变,在事件监听器中,this默认指向事件所绑定的元素
- call与bind的区别:call会调用函数,而bind只是返回新绑定的函数,不会调用
lufthansa.planes = 300;
lufthansa.buyPlane = function () {
console.log(this);
this.planes++;
console.log(this.planes);
};
document
.querySelector('.buy')
.addEventListener('click', lufthansa.buyPlane.bind(lufthansa));
部分应用:partial application
不用于绑定this,而是用于设置预设参数
// Partial application
const addTax = (rate, value) => value + value * rate;
console.log(addTax(0.1, 200));
const addVAT = addTax.bind(null, 0.23);
// 相当于 addVAT = value => value + value * 0.23;
console.log(addVAT(100));
///
//使用函数返回函数的方式
const addTaxRate = function (rate) {
return function (value) {
return value + value * rate;
};
};
const addVAT2 = addTaxRate(0.23);
console.log(addVAT2(100));
console.log(addVAT2(23));
立即调用函数表达式:Immediately Invoked Function Expressions (IIFE)
用圆括号将函数括起来,形成一个表达式,再在后面加一个括号,即可调用这个函数。这个函数在执行后即被销毁,其中变量也无法继续生存。(但可以使用闭包访问)
- 使用目的:数据封装
(function () {
console.log('This will never run again');
const isPrivate = 23;
})();
/
(() => console.log('This will ALSO never run again'))();
数据封装的另一个方式:
在一个代码块内使用let
与const
在代码块内使用var会建立一个全局变量,因此var定义变量不是很安全,应该少用
闭包 Closures
简单举例
// Closures
const secureBooking = function () {
let passengerCount = 0;//pC是这个函数中的局部变量
return function () {
passengerCount++;//在此处自增
console.log(`${passengerCount} passengers`);
};
};
const booker = secureBooking();//此时应返回secureBooking中的function(),其中的pC应失去其上下文(从栈中弹出)
/调用
booker();//输出1
booker();//输出2
booker();//输出3=>证明pC在本来已经不再上下文的调用中实现了自增,这就是闭包
console.dir(booker);//查看属性
闭包就是封闭变量
的执行上下文
的环境中所创建的函数
,在上下文消失后,调用函数,封闭变量
仍可被访问到`。
- 解释:就像函数存在一个不离身的背包,这个背包中是其父环境中所定义的变量。
- 或:像一个人(函数)永远与他的家乡(父环境)存在不断的联系,能回家拿东西。
- 闭包无法被手动创建,其中的变量也无法被显式修改
booker这一函数可以访问 passengerCount的上下文,因为booker的定义处是secureBooking的上下文范围,尽管secureBooking出栈了,但是它其中的参数被永远保留在了闭包中可被调用到。
闭包中定义的元素其优先级高于在作用域链(scope chain)定义元素的优先级,即若存在全局变量passengerCount,在调用booker时依然会使用闭包中的passengerCount。
闭包可被修改“指向”
let f;
const g = function () {
const a = 23;
f = function () {
console.log(a * 2);
};
};
const h = function () {
const b = 777;
f = function () {
console.log(b * 2);
};
};
g();
f();
console.dir(f);
// Re-assigning f function
h();
f();
console.dir(f);
短路特性的巧用
要执行的只有一句话,此时无需写if条件,直接将条件与执行的程序用&&
连起来,这样利用短路,只要条件不符合,则无法执行。
typeof answer === 'number' && answer < this.answers.length && this.answers[answer]++;