好程序员web前端分享详细了解JavaScript函数

好程序员web前端分享详细了解JavaScript函数如果你曾经接触过JavaScript编程,你一定不会陌生如何定义并且调用一个函数。但是你知道在JavaScript中有多少种定义函数的方法吗?如果想要在Test262中编写和维护这些方法的测试,那可真是一个很大的挑战,尤其当一些新特性和现有函数语法相关,或者扩展了函数的API时。但是,想要断言新提出或被提案的语法、API有效时,测试所有既存变式又是非常必要的。 下面会针对JavaScript中已经存在的函数定义方式进行一个概述。本文不包含Class声明和表达式,因为这些方式创建的对象是“不可调用的”,本文旨在那些可生成“可调用”对象的函数定义方式。也就是说,我们不会研究那些复杂的参数列表(包含默认参数、结构赋值或者尾后逗号),因为那足够另起文章介绍了。

 

以前的方式

 

函数声明以及函数表达式

 

最为出名以及应用最广的同样也是这些旧方式:函数声明和函数表达式。前者设计(1995)和出现在第一版的规范(1997)(pdf)中。后者则是出现在第三版中(1999(pdf)。仔细研究,你会从它们当中提取出三种不同的方式。

 

// 函数声明

function BindingIdentifier() {}

 

扫描二维码关注公众号,回复: 5842816 查看本文章

// 命名函数表达式

// (BindingIdentifier在函数外部是访问不到的)

(function BindingIdentifier() {});

 

// 匿名函数表达式

(function() {});

值得注意的是,匿名函数表达式仍然可能有名字,Mike Pennisi在什么是函数名称?中有深度解释。

 

Function构造函数

 

当研究一门语言的"function API"的时候,也就到了这门语言的底层。在这门语言的设计之初,函数声明方式可以被理解为是Function构造函数API的最直接实现。Function构造函数提供了一种定义函数的方式:通过指明Function的参数,其中最后一个参数就是函数的函数体(必须要说明的是,这是一种动态代码方式,可能存在安全问题)。在大多数情况下,这种方式是不合适的,所以用的人很少,但是在第一版的ECMAScript中,这种方式就出现了。

 

new Function('x', 'y', 'return x ** y;');

新的方式

 

自从ES2015发布以来,几种新的定义函数方式被引入进来,这些方式的变式更是非常繁多。

 

另类匿名函数

 

这是一种新式的匿名函数。如果你曾经接触过ES的模块化,那么你很有可能已经接触过这种定义函数的方式了。尽管这种方式看起来和匿名函数的定义方式很像,但是他确实有自己的名字:default

 

// 另类匿名函数声明

export default function() {}

顺便一提,这个名字并不是专属的标识,并没有进行绑定。

 

方法定义

 

下面这些方式定义的函数表达式、匿名函数或者命名函数,都是某个对象的属性。注意这些并不是新的语法,只是应用上面提及的那些语法,写在了某个对象的初始化器中。这种方式最早引入在ES3中。

 

let object = {

  propertyName: function() {},

};

let object = {

  // (BindingIdentifier不能再函数外部调用)

  propertyName: function BindingIdentifier() {},

};

下面是存取器属性,引入在ES5

 

let object = {

  get propertyName() {},

  set propertyName(value) {},

};

ES2015中,JavaScript中提供了一种定义方法的简洁语法,不管是直接命名的方式还是计算属性名的方式,都可以使用,而且,存取器同样适用。

 

let object = {

  propertyName() {},

  ["computedName"]() {},

  get ["computedAccessorName"]() {},

  set ["computedAccessorName"](value) {},

};

你也可以把这些定义属性或者方法的新方式应用在创建类时。

 

// 类声明

class C {

  methodName() {}

  ["computedName"]() {}

  get ["computedAccessorName"]() {}

  set ["computedAccessorName"](value) {}

}

 

// 类表达式

let C = class {

  methodName() {}

  ["computedName"]() {}

  get ["computedAccessorName"]() {}

  set ["computedAccessorName"](value) {}

};

...在定义静态方法时,同样可以使用。

 

// 类声明

class C {

  static methodName() {}

  static ["computedName"]() {}

  static get ["computedAccessorName"]() {}

  static set ["computedAccessorName"](value) {}

}

 

// 类表达式

let C = class {

  static methodName() {}

  static ["computedName"]() {}

  static get ["computedAccessorName"]() {}

  static set ["computedAccessorName"](value) {}

};

箭头函数

 

箭头函数首次出现在ES2015中,尽管起初饱受争议,但是现在已经被广泛应用了。箭头函数的定义根据是否简写有两种不同的语法:赋值表达式(在箭头后面没有花括号)和函数体(当函数包含零个或者多个表达式时)。语法规定,当函数只有一个参数时,可以不用小括号括起来,但是当没有参数或者多余一个参数时,就必须用小括号括起来了。(这种语法就决定了箭头函数会有多种定义形式)

 

// 零参数, 赋值表达式

(() => 2 ** 2);

 

// 一个参数, 可以省略小括号, 赋值表达式

(x => x ** 2);

 

// 一个参数, 可以省略小括号, 函数体

(x => { return x ** 2; });

 

// 多个参数, 赋值表达式

((x, y) => x ** y);

上面的最后一种形式中,参数是用参数列表来表示的,因为它们用小括号括了起来。类似于用小括号来标识参数列表的语法,还有其他形式,诸如({ x }) => x。 如果参数不用小括号括起来,那么只能给参数起一个独一的标识符名称,以在箭头函数中使用。当箭头函数被定义为异步函数或者Generator函数时,这个标识符名称还可以加上await 或者 yield的前缀,但那也已经是在不用括号情形中,考虑足够深远的了。 箭头函数可以并且也经常出现在初始化器或者对象属性的定义中,但是这种情况大部分使用的是上面介绍的赋值表达式形式,举例如下:

 

let foo = x => x ** 2;

 

let object = {

  propertyName: x => x ** 2

};

Generators

 

Generator函数的语法是在其他定义函数的方式上加点东西,但箭头函数和存取器方法除外。你可以使用和之前函数声明,函数表达式,函数定义甚至是构造函数等相似的方式。所有方法列举如下:

 

// Generator 声明

function *BindingIdentifer() {}

 

// 另类匿名 Generator 声明

export default function *() {}

 

// Generator 表达式

// (BindingIdentifier只能在函数内部调用)

(function *BindingIdentifier() {});

 

// 匿名 Generator 表达式

(function *() {});

 

// 方法定义

let object = {

  *methodName() {},

  *["computedName"]() {},

};

 

// 在类声明中定义方法

class C {

  *methodName() {}

  *["computedName"]() {}

}

 

// 在类声明中定义静态方法

class C {

  static *methodName() {}

  static *["computedName"]() {}

}

 

// 在类表达式中定义方法

let C = class {

  *methodName() {}

  *["computedName"]() {}

};

 

// 在类表达式中定义静态方法

let C = class {

  static *methodName() {}

  static *["computedName"]() {}

};

ES2017

 

异步函数

 

经过几年的发展,异步函数将会发布ES2017——第八版EcmaScript语言规范——规范会在20176月在正式发布。但其实,很多开发者早已经开始使用异步函数了,这还要归功于Babel的支持。 异步函数语法提供了一个干净的、统一的方式来描述异步操作。当被调用时,异步函数会返回一个Promise对象。当异步执行结束后,这个Promise对象即会被相应执行。当函数中含有await表达式时,异步函数就会暂停执行,这时,await表达式结果就会作为异步函数的返回值。 异步函数的语法并没有太多的不同,只是在我们熟知的那些方式前面加上一个前缀:

 

//  异步函数声明

async function BindingIdentifier() { /**/ }

 

// 另类匿名异步函数声明

export default async function() { /**/ }

 

// 命名异步函数表达式

// (BindingIdentifier只能在函数内部调用)

(async function BindingIdentifier() {});

 

// 匿名异步函数表达式

(async function() {});

 

// 异步方法

let object = {

  async methodName() {},

  async ["computedName"]() {},

};

 

// 类声明中的异步方法

class C {

  async methodName() {}

  async ["computedName"]() {}

}

 

// 类声明中的静态异步方法

class C {

  static async methodName() {}

  static async ["computedName"]() {}

}

 

// 类表达式中的异步方法

let C = class {

  async methodName() {}

  async ["computedName"]() {}

};

 

// 类表达式中的静态异步方法

let C = class {

  static async methodName() {}

  static async ["computedName"]() {}

};

异步箭头函数

 

async await并不是只局限在常规函数的声明或者表达式中,它们同样适用于箭头函数:

 

// 单一参数,赋值表达式

(async x => x ** 2);

 

// 单一参数,函数体

(async x => { return x ** 2; });

 

// 参数列表,赋值表达式

(async (x, y) => x ** y);

 

// 参数列表,函数体

(async (x, y) => { return x ** y; });

ES2017结合

 

异步Generator函数

 

ES2017结合,async await将会被扩展支持异步函数。这一特性的发展可以追踪推荐的github仓储。正如你猜想到的,异步Generator函数的语法是结合asyncawait以及已经存在的Generator函数声明和表达式而来。当异步Generator函数被调用的时候,会返回一个迭代器,这个迭代器的next()方法会返回一个Promise对象来处理迭代器的返回对象,而不是直接返回迭代器的结果对象。 异步Generator函数已经开始在很多地方使用,你很有可能已经碰到过。

 

// 异步Generator声明

async function *BindingIdentifier() { /**/ }

 

// 另类匿名异步Generator声明

export default async function *() {}

 

// 异步Generator表达式

// (BindingIdentifier只能在函数内部访问)

(async function *BindingIdentifier() {});

 

// 匿名异步Generator表达式

(async function *() {});

 

// 匿名异步Generator方法定义

let object = {

  async *propertyName() {},

  async *["computedName"]() {},

};

 

// 类声明中异步Generator原型方法定义

class C {

  async *propertyName() {}

  async *["computedName"]() {}

}

 

// 类表达式中异步Generator原型方法定义

let C = class {

  async *propertyName() {}

  async *["computedName"]() {}

};

 

// 类声明中异步Generator静态方法定义

class C {

  static async *propertyName() {}

  static async *["computedName"]() {}

}

 

// 类表达式中异步Generator静态方法定义

let C = class {

  static async *propertyName() {}

  static async *["computedName"]() {}

};


猜你喜欢

转载自blog.51cto.com/14249543/2376789