资讯专栏INFORMATION COLUMN

ES6语法之函数的扩展

Clect / 2865人阅读

摘要:函数的扩展函数参数的默认值之前,不能直接为函数的参数指定默认值,只能采用变通的方法。箭头函数引入了一种新的函数,叫做箭头函数。箭头函数和普通函数的行为非常相似,但是在语法构成上非常不同。意味着函数内的的值是全局对象,不是对象。

函数的扩展 函数参数的默认值

ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

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

上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋的值对应的布尔值为false,则该赋值不起作用。

function greet(name, greeting) {
  name = (typeof name !== "undefined") ?  name : "Student";
  greeting = (typeof greeting !== "undefined") ?  greeting : "Welcome";

  return `${greeting} ${name}!`;
}

greet(); // Welcome Student!
greet("James"); // Welcome James!
greet("Richard", "Howdy"); // Howdy Richard!

为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。

if (typeof y === "undefined") {
  y = "World";
}

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

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

log("Hello") // Hello World
剩余参数
剩余参数也用三个连续的点 ( ... ) 表示,使你能够将不定数量的元素表示为数组。
与解构赋值结合

剩余参数可以与解构赋值结合起来,用于生成数组。

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

可以将剩余参数看着展开运算符的对立面;如果展开运算符是拿出包装盒中的所有物品,那么剩余参数就是将所有物品放回包装盒中。

可变参数函数

剩余参数的另一个用例是处理可变参数函数。可变参数函数是接受不定数量参数的函数。

例如,假设有一个叫做 sum() 的函数,它会计算不定数量的数字的和。在运行期间,如何调用 sum() 函数?

sum(1, 2);
sum(10, 36, 7, 84, 90, 110);
sum(-23, 3000, 575000);

实际上有无数种方式可以调用 sum() 函数。不管传入函数的数字有多少个,应该始终返回数字的总和。

使用参数对象

在之前版本的 JavaScript 中,可以使用参数对象处理这种类型的函数。参数对象是像数组一样的对象,可以当做本地变量在所有函数中使用。它针对传入函数的每个参数都包含一个值,第一个参数从 0 开始,第二个参数为 1,以此类推。

function sum() {
  let total = 0;  
  for(const argument of arguments) {
    total += argument;
  }
  return total;
}

现在可以正常运行,但是存在问题:

如果查看 sum() 函数的定义,会发现它没有任何参数。这容易引起误导,因为我们知道 sum() 函数可以处理不定数量的参数。

难以理解。

如果你从未使用过参数对象,那么看了这段代码后很可能会疑问参数对象来自何处。是不是凭空出现的?看起来肯定是这样。

使用剩余参数

function sum(...nums) {
  let total = 0;  
  for(const num of nums) {
    total += num;
  }
  return total;
}
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
箭头函数
ES6 引入了一种新的函数,叫做箭头函数。箭头函数和普通函数的行为非常相似,但是在语法构成上非常不同。

普通函数可以是函数声明或函数表达式,但是箭头函数始终是表达式。实际上,它们的全称是“箭头函数表达式”

//普通函数
const upperizedNames = ["Farrin", "Kagure", "Asser"].map(function(name) { 
  return name.toUpperCase();
});

//箭头函数
const upperizedNames = ["Farrin", "Kagure", "Asser"].map(
  name => name.toUpperCase()
);

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

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

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};

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

var sum = (num1, num2) => { return num1 + num2; }
箭头函数中的this
对于普通函数,this 的值基于函数如何被调用。对于箭头函数,this 的值基于函数周围的上下文。换句话说,箭头函数内的,this 的值与函数外面的 this 的值一样。

我们先看一个普通函数中的 this 示例,再看一个箭头函数是如何使用 this 的。

// constructor
function IceCream() {
  this.scoops = 0;
}

// adds scoop to ice cream
IceCream.prototype.addScoop = function() {
  setTimeout(function() {
    this.scoops++;
    console.log("scoop added!");
  }, 500);
};

const dessert = new IceCream();
dessert.addScoop();

运行上述代码后,你会认为半毫秒之后,dessert.scoops 会是1。但并非这样:结果是NaN

这是因为传递给 setTimeout() 的函数被调用时没用到 new、call() 或 apply(),也没用到上下文对象。意味着函数内的 this 的值是全局对象,不是 dessert 对象。实际上发生的情况是,创建了新的 scoops 变量(默认值为 undefined),然后递增(undefined + 1 结果为 NaN

// constructor
function IceCream() {
  this.scoops = 0;
}

// adds scoop to ice cream
IceCream.prototype.addScoop = function() {
  setTimeout(() => { // an arrow function is passed to setTimeout
    this.scoops++;
    console.log("scoop added!");
  }, 0.5);
};

const dessert = new IceCream();
 
console.log(dessert.scoops); //1

因为箭头函数从周围上下文继承了 this 值,所以这段代码可行!

如果我们将 addScoop() 方法也改为箭头函数,你认为会发生什么?

// constructor
function IceCream() {
    this.scoops = 0;
}

// adds scoop to ice cream
IceCream.prototype.addScoop = () => { // addScoop is now an arrow function
  setTimeout(() => {
    this.scoops++;
    console.log("scoop added!");
  }, 0.5);
};

const dessert = new IceCream();
dessert.addScoop();

这段代码因为同一原因而不起作用,即箭头函数从周围上下文中继承了 this 值。在 addScoop() 方法外面,this 的值是全局对象。因此如果 addScoop() 是箭头函数,addScoop() 中的 this 的值是全局对象。这样的话,传递给 setTimeout() 的函数中的 this 的值也设为了该全局对象!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90680.html

相关文章

  • ES6学习手稿基本类型扩展

    摘要:它是一个通用标准,奠定了的基本语法。年月发布了的第一个版本,正式名称就是标准简称。结语的基本扩展还有一些没有在这里详细介绍。 前言 ES6标准以及颁布两年了,但是,好像还没有完全走进我们的日常开发。这篇文章从ES6的基本类型扩展入手,逐步展开对ES6的介绍。 ECMAScript和JavaScript JavaScript是由Netscape创造的,该公司1996年11月将JavaSc...

    tommego 评论0 收藏0
  • ES6语法字符串扩展

    摘要:模板字符串连接符在之前,将字符串连接到一起的旧方法是使用字符串连接运算符。这样更容易构建字符串。返回布尔值,表示参数字符串是否在原字符串的头部。它针对前个字符,而其他两个方法针对从第个位置直到字符串结束。 模板字符串 + 连接符 在 ES6 之前,将字符串连接到一起的旧方法是使用字符串连接运算符 (+)。 const student = { name: Richard Kaleho...

    孙淑建 评论0 收藏0
  • 深入理解ES6《用模块封装代码》

    摘要:导入模块的代码执行后,实例化过的模块被保存在内存中,只要另一个语句引用它就可以重复使用它和的一个重要的限制是它们必须在其它语句和函数之外使用,也就是说不允许出现在语句中,不能有条件导出或以任何方式动态导出。 什么是模块 模块是自动运行在严格模式下并且没有办法退出运行的Javascript代码 在模块的顶部this的值是undefined 其模块不支持html风格的代码注释除非用def...

    BigTomato 评论0 收藏0
  • 深入理解ES6《用模块封装代码》

    摘要:导入模块的代码执行后,实例化过的模块被保存在内存中,只要另一个语句引用它就可以重复使用它和的一个重要的限制是它们必须在其它语句和函数之外使用,也就是说不允许出现在语句中,不能有条件导出或以任何方式动态导出。 什么是模块 模块是自动运行在严格模式下并且没有办法退出运行的Javascript代码 在模块的顶部this的值是undefined 其模块不支持html风格的代码注释除非用def...

    muddyway 评论0 收藏0
  • 深入理解ES6《用模块封装代码》

    摘要:导入模块的代码执行后,实例化过的模块被保存在内存中,只要另一个语句引用它就可以重复使用它和的一个重要的限制是它们必须在其它语句和函数之外使用,也就是说不允许出现在语句中,不能有条件导出或以任何方式动态导出。 什么是模块 模块是自动运行在严格模式下并且没有办法退出运行的Javascript代码 在模块的顶部this的值是undefined 其模块不支持html风格的代码注释除非用def...

    AbnerMing 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<