es6-函数的扩展

news/2024/7/10 23:02:49 标签: es6, 函数

参照:http://es6.ruanyifeng.com/#docs/function

1.函数参数的默认值

function log(x, y) {
  y = y || 'World';
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World  这有个缺点,y 为空字符则被改为默认值,这需要设置y的默认值
function log(x, y = 'World') {
  console.log(x, y);
}

log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello

2.与解构赋值默认值结合使用

function foo({x, y = 5}) {
  console.log(x, y);
}

foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo()
 // TypeError: Cannot read property 'x' of undefined

只有当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数foo调用时没提供参数,变量x和y就不会生成,从而报错。

3.参数默认值的位置

定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

// 例一
function f(x = 1, y) {
  return [x, y];
}

f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]

// 例二
function f(x, y = 5, z) {
  return [x, y, z];
}

f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]

4.函数的length属性

指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1

5.rest参数

es6引入rest数组参数来代替arguments对象,可以省好多事情。
例如可以直接调用数组的方法,而不用转换。

// arguments变量的写法
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();

arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组。rest
参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

5.严格模式

ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

两种方法可以规避这种限制。第一种是设定全局性的严格模式,这是合法的。

'use strict';

function doSomething(a, b = a) {
  // code
}

第二种是把函数包在一个无参数的立即执行函数里面。

const doSomething = (function () {
  'use strict';
  return function(value = 42) {
    return value;
  };
}());

6.箭头函数

var f = v = >v;
//等同于
var f = function (v){
return v;
}

如果箭头函数不需要参数或者需要多个参数,就用一个圆括号代表参数部分。

var f = () =>v;
//等同于
var f = function(){
return v;
}

var f = (n1,n2) =>n1+n2;
//等同于
var f = function(n1,n2){
return n1+n2;
}

如果箭头函数代码块部分多余一条语句,就要用大括号将他们括起来,并且用return 语句返回。

var sum = (num1,num2) =>{return num1 + num2;}

由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

箭头函数的一个用处是简化回调函数

//正常函数写法
[1,2,3].map(function(x){
return x*x;
});
//箭头函数写法
[1,2,3].map(x => x * x);

另一个例子

    //正常函数写法
var result = values.sort(function(a,b){
return a - b;
});
    //箭头函数写法
   var result = values.sort((a,b)=>a-b);

rest参数与箭头函数结合的例子

const numbers = (...nums) =>nums;
numbers(1,2,3,4,5); // [1,2,3,4,5]

const headAndTail = (head, ...tail) =>[head,tail];
headAndTail(1,2,3,4,5); //[1,[2,3,4,5]]

使用注意点 箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数
注意:this对象的指向是可变的,但是在箭头函数中,它是固定的。

箭头函数可以让this指向固定化,这种特性有利于封装回调函数

var Handler = {
	id:'123',
	init: function(){
		document.addEventListener('click',event => this.doSomething(event.type),false);
	},
	doSomething:function(type){
		console.log('Handing' + type + 'for' + this.id);
	}
};

上面的init方法中,箭头函数里面的this总是指向handler对象。
this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正因为它没有this,所以也不能用作构造函数

箭头函数转成ES5的代码如下:

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}

除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target。
由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。

不适用场合:
由于箭头函数使得this从动态变为静态,下面不适合用
1.定义函数的方法,且该方法内部包括this
2.需要动态this的时候

7.双冒号运算符

箭头函数可以绑定this对象,大大减少了显示绑定this对象的写法(call,apply,bind),但是箭头函数并不适用于所有场合,所以现在有一个提案,绑定函数运算符。
两个冒号:(:?;两个冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

foo::bar;
// 等同于
bar.bind(foo);

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

如果冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面。

var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;

let log = ::console.log;
// 等同于
var log = console.log.bind(console);

8.尾调用优化

指某个函数的最后一步是调用另一个函数

function f(x){
  return g(x);
}

以下三种情况不属于尾调用

// 情况一
function f(x){
  let y = g(x);
  return y;
}

// 情况二
function f(x){
  return g(x) + 1;
}

// 情况三
function f(x){
  g(x);
}

尾调用不一定出现在函数尾部,只要是最后一步操作即可。

function f(x) {
  if (x > 0) {
    return m(x)
  }
  return n(x);
}

尾递归

递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120
//计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。

改为尾递归:

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120
//只保留一个调用记录,复杂度 O(1) 。

递归函数的改写

尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数。比如上面的例子,阶乘函数 factorial 需要用到一个中间变量total,那就把这个中间变量改写成函数的参数。这样做的缺点就是不太直观,第一眼很难看出来,为什么计算5的阶乘,需要传入两个参数5和1?

function tailFactorial(n, total) {
  if (n === 1) return total;
  return tailFactorial(n - 1, n * total);
}

function factorial(n) {
  return tailFactorial(n, 1);
}

factorial(5) // 120

第二种方法就简单多了,就是采用ES6的函数默认值。

function factorial (n,total = 1){
	if(n === 1) return total;
	return factorial(n - 1,n*total);
}
factorial(5); // 120

严格模式

ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。
这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈,
func.arguments:返回调用时函数的参数。
func.caller:返回调用当前函数的那个函数

尾调用优化发生时,函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效。

function restricted() {
  'use strict';
  restricted.caller;    // 报错
  restricted.arguments; // 报错
}
restricted();

http://www.niftyadmin.cn/n/773837.html

相关文章

es6-数组的扩展

http://es6.ruanyifeng.com/?searchimport&x0&y0#docs/array 1.扩展运算符 扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。 console.log(...[1, 2, 3])…

移动端页面适配方案

https://blog.csdn.net/caibinghong/article/details/84802267 https://blog.csdn.net/sophie_u/article/details/76223029

也许未来,不是河流不是山脉

这已经是我大学的最后一个学期了,好久没有好好问问自己想要的生活是什么样了,以前的初心还在吗? 当我打开编辑器,首先想到的就是这句话,想起来了这首歌。 致跑灯花 最近公司在在赶一个项目,所以自从年后来上…

java for(o t :object) 获取顺序号_Java中的动态代理

Java中的动态代理说起动态代理,首先想到的就是Spring,在Spring中有两种动态代理方式:JDK动态代理和Cglib动态代理。JDK动态代理JDK动态代理是Java本来就有的一种代理方式,关键类是java.lang.reflect.InvocationHandler。我们先创建…

mui 解决app调起手机键盘时,界面内容向上移动

<!--防止软键盘调起时&#xff0c;底部内容随之挤上来--> <script type"text/javascript">//获取原始窗口的高度var originalHeight document.documentElement.clientHeight || document.body.clientHeight;window.onresize function() {//软键盘弹起与…

深度清理命令_万字干货深度解析 JVM

工作之余&#xff0c;想总结一下JVM相关知识。Java运行时数据区&#xff1a;Java虚拟机在执行Java程序的过程中会将其管理的内存划分为若干个不同的数据区域&#xff0c;这些区域有各自的用途、创建和销毁的时间&#xff0c;有些区域随虚拟机进程的启动而存在&#xff0c;有些区…

给我

最近听了一首歌&#xff0c;毛不易的&#xff0c;《给你给我》&#xff0c;特别好听&#xff0c;很令人感动。 我快毕业了&#xff0c;现在在着急忙慌的写论文&#xff0c;写的是在公司的的一项目&#xff0c;其实这个项目的后台我参与的并不是很多&#xff0c;当初我主要开发的…

nodejs--helloworld入门

推荐一首特别好听得歌《清平调》&#xff0c;邓丽君和王菲的那一版&#xff0c;特别好听。 https://music.163.com/song?id31917312&userid411200993 最近在学习node.js. 1.windows 上64位安装 下载&#xff1a;64 位安装包下载地址 : https://nodejs.org/dist/v4.4.3/…