ES6之主要知识点(五)函数
函数参数的默认值
作用域
var x = ;
function f(x, y = x) {
console.log(y);
}
f() //
let x = ;
function f(y = x) {
let x = ;
console.log(y);
}
f() //
var x = ;
function foo(x, y = function() { x = ; }) {
var x = ;
y();
console.log(x);
} foo() //
x //
var x = ;
function foo(x, y = function() { x = ; }) {
x = ;
y();
console.log(x);
} foo() //
x //
参数变量是默认声明的,所以不能用let或const再次声明。
使用参数默认值时,函数不能有同名参数
应用
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
上面代码的foo函数,如果调用的时候没有参数,就会调用默认值throwIfMissing函数,从而抛出一个错误。
2.rest 参数
ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。
rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
3.箭头函数
x => x * x
上面的箭头函数相当于:
function (x) {
return x * x;
}
如果参数不是一个,就需要用括号()括起来:
// 两个参数:
(x, y) => x * x + y * y // 无参数:
() => 3.14 // 可变参数:
(x, y, ...rest) => {
var i, sum = x + y;
for (i=; i<rest.length; i++) {
sum += rest[i];
}
return sum;
}
如果要返回一个对象,就要注意,如果是单表达式,这么写的话会报错:
// SyntaxError:
x => { foo: x }
因为和函数体的{ ... }有语法冲突,所以要改为:
// ok:
x => ({ foo: x })
箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。
回顾前面的例子,由于JavaScript函数对this绑定的错误处理,下面的例子无法得到预期结果:
var obj = {
birth: ,
getAge: function () {
var b = this.birth; //
var fn = function () {
return new Date().getFullYear() - this.birth; // this指向window或undefined
};
return fn();
}
现在,箭头函数完全修复了this的指向,this总是指向词法作用域,也就是外层调用者obj:
var obj = {
birth: ,
getAge: function () {
var b = this.birth; //
var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
return fn();
}
};
obj.getAge(); //
使用注意点
箭头函数有几个使用注意点。
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
function Timer() {
this.s1 = ;
this.s2 = ;
// 箭头函数
setInterval(() => this.s1++, );
// 普通函数
setInterval(function () {
this.s2++;
}, );
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), );
setTimeout(() => console.log('s2: ', timer.s2), );
// s1: 3
// s2: 0
上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数)
,后者的this指向运行时所在的作用域(即全局对象)。所以,3100毫秒之后,timer.s1被更新了3次,而timer.s2一次都没更新。
另外,由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。
4.绑定 this
函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,
作为上下文环境(即this对象),绑定到右边的函数上面。
foo::bar;
// 等同于
bar.bind(foo); foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments); const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}
5.尾调用优化
尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
function f(x) {
if (x > 0) {
return m(x)
}
return n(x);
}
尾递归
function factorial(n) {
if (n === ) return ;
return n * factorial(n - );
}
factorial() //
上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。
function factorial(n, total) {
if (n === ) return total;
return factorial(n - , n * total);
}
factorial(, ) //
严格模式
ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。
这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。
func.arguments:返回调用时函数的参数。func.caller:返回调用当前函数的那个函数。
尾调用优化发生时,函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效
在非严格模式时候我们可以自己模拟
蹦床函数(trampoline)可以将递归执行转为循环执行。
function trampoline(f) {
while (f && f instanceof Function) {
f = f();
}
return f;
}
上面就是蹦床函数的一个实现,它接受一个函数f作为参数。只要f执行后返回一个函数,就继续执行。
注意,这里是返回一个函数,然后执行该函数,而不是函数里面调用函数,这样就避免了递归执行,从而就消除了调用栈过大的问题。
然后,要做的就是将原来的递归函数,改写为每一步返回另一个函数。
function sum(x, y) {
if (y > ) {
return sum.bind(null, x + , y - );
} else {
return x;
}
}
上面代码中,sum函数的每次执行,都会返回自身的另一个版本。
现在,使用蹦床函数执行sum,就不会发生调用栈溢出。
trampoline(sum(, ))
//
蹦床函数并不是真正的尾递归优化,下面的实现才是。
function tco(f) {
var value;
var active = false;
var accumulated = [];
return function accumulator() {
accumulated.push(arguments);
if (!active) {
active = true;
while (accumulated.length) {
value = f.apply(this, accumulated.shift());
}
active = false;
return value;
}
};
}
var sum = tco(function(x, y) {
if (y > ) {
return sum(x + , y - )
}
else {
return x
}
});
sum(, )
//
ES6之主要知识点(五)函数的更多相关文章
- es6的一些知识点
es6的一些知识点 前言:es6(ECMAscript2015)标准 let.const.var的一些区别 let.const 块级作用域.全局作用域.函数作用域 var 全局作用域.函数作用域 变量 ...
- ES6学习笔记<三> 生成器函数与yield
为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...
- TS学习随笔(五)->函数
这篇文章我们来看一下TS里面的函数 函数声明 在 JavaScript 中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expre ...
- PostgreSQL学习手册(五) 函数和操作符
PostgreSQL学习手册(五) 函数和操作符 一.逻辑操作符: 常用的逻辑操作符有:AND.OR和NOT.其语义与其它编程语言中的逻辑操作符完全相同. 二.比较操作符: 下面是Post ...
- 石川es6课程---4、箭头函数
石川es6课程---4.箭头函数 一.总结 一句话总结: 相当于函数的简写,类似python lambda 函数,先了解即可 let show1 = function () { console.log ...
- ES6学习总结(五)
与其说是对象合并,还不如说是JavaScript中对象属性的复制和转移,将多个对象中的属性合并到一个对象中 12345678 var person = { name : 'John', age : 2 ...
- 《理解 ES6》阅读整理:函数(Functions)(五)Name Property
名字属性(The name Property) 在JavaScript中识别函数是有挑战性的,因为你可以使用各种方式来定义一个函数.匿名函数表达式的流行使用导致函数调试困难,在栈信息中难以找出函数名. ...
- ES6学习笔记(五)函数的扩展
1.函数参数的默认值 1.1基本用法 ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法. function log(x, y) { y = y || 'World'; console. ...
- ES6标准入门 第五章:函数的扩展
1.函数参数的默认值 (1)基本用法 ES5 中, 不能直接为函数的参数指定默认值.只能采用变通的方法. function log(x, y) { y = y || 'World'; console. ...
随机推荐
- Algo: Two Sum
类似的题目可以用HashTable的思想解决. 1.Two Sum Given an array of integers, return indices of the two numbers such ...
- 关于 第三方接口支付的时候 采用post提交的方式,有两种 一种是通过 curl来进行,一种是通过js当页面加载完后跳转
这是第一种.通过javascript页面加载完后,对表单采用 post方式提交给 第三方接口----- echo <<<_END<!DOCTYPE html PUBLIC &q ...
- Java怎样获取字符串最后出现的位置
lastIndexOf();表示获取字符串最后出现的位置,倒数的位置 @Test /** * lastIndexOf();//获取字符串最后出现的位置,倒数的位置 * */ public void f ...
- hdu多校第二场 1005 (hdu6595) Everything Is Generated In Equal Probability
题意: 给定一个N,随机从[1,N]里产生一个n,然后随机产生一个n个数的全排列,求出n的逆序数对的数量,加到cnt里,然后随机地取出这个全排列中的一个非连续子序列(注意这个子序列可以是原序列),再求 ...
- HDU 6064 RXD and numbers
传送门 有向图生成树计数 (度数 ->入度->外向树) BEST定理 (不定起点的欧拉回路个数=某点为根的外向树个数(存在欧拉回路->每个点为根的外向树个数相等)*(每个点的度数(存 ...
- window.location 对象中各种方法的用途
一.简介 属性 描述 hash 从井号 (#) 开始的 URL(锚) host 主机名和当前 URL 的端口号 hostname 当前 URL 的主机名 href 完整的 URL pathname 当 ...
- memcpy函数实现中的优化
今天浏览Google面试题的时候,有看到一个memcpy的实现,以及如何去优化memcpy. 我对memcpy的实现的记忆就是,拷贝的时候需要从后往前拷贝,为何防止内存重叠. 但是如果去优化它我没有想 ...
- Python 实现0-1背包
代码: import numpy as np c=10 #背包容量 w=[2,2,6,5,4] #物品重量 v=[5,3,5,4,6] #物品价值 flag =[0,0,0,0,0] m=np.zer ...
- k8s 安装
1.1 k8s的架构 除了核心组件,还有一些推荐的Add-ons: 组件名称 说明 kube-dns 负责为整个集群提供DNS服务 Ingress Controller 为服务提供外网入口 Heaps ...
- 第二十篇:记下第一个mysql触发器
项目背景:给一个服务限制访问次数,当用户访问这个服务的次数达到这个值的时候,关闭他的访问权限首先访问信息存在一张表中,记录用户的ip:visitor_ip,服务的id:service_id,访问次数: ...