web之路让前端飞程序员

【十三】JavaScript执行(三):你知道现在有多少种函数吗

2019-03-06  本文已影响7人  alanwhy

函数

在ES2018中,函数已经是一个很复杂的体系了,我在这里整体了一下。

1、普通函数:用function关键字定义的函数
function foo(){
    // code
}
2、箭头函数:用=>运算符定义的函数
const foo = () => {
    // code
}
3、方法:在class中定义的函数
class C {
    foo(){
        //code
    }
}
4、生成器函数,用function*定义的函数
function foo*(){
    // code
}
5、类:用class定义的类,实际上也是函数
class Foo {
    constructor(){
        //code
    }
}

6-8、异步函数:普通函数、箭头函数、生成器函数加上async关键字

async function foo(){
    // code
}
const foo = async () => {
    // code
}
async function foo*(){
    // code
}

对于普通变量而言,这些函数没有本质的区别,都遵循了“继承定义时环境”的规则,他们的一个行为差异在于this关键字

this 关键字的行为

this 是执行上下文中很重要的一个组成部分。同一个函数调用方式不同,得到的this值也不同

function showThis(){
    console.log(this);
}

var o = {
    showThis: showThis
}

showThis(); // global
o.showThis(); // o

普通函数的this值由“调用它所使用的引用”决定,其中奥秘就在于:我们获取函数的表达式,实际上返回的并非函数本身,而是一个Reference类型

Reference 类型由两部分组成:一个对象和一个属性值。

不难理解o.showThis产生的Reference类型,即由对象o和属性“showThis”构成。

当做一些算术运算(或者其他运算时),Reference 类型会被解引用,即获取真正的值(被引用的内容)来参与运算,而类似函数调用、delete 等操作,都需要用到Reference类型中的对象。

在这个例子中,Reference 类型中的对象被当作this值,传入了执行函数时的上下文当中。

至此,我们对this的解释已经非常清晰了:调用函数时使用的引用,决定了函数执行时刻的this值。

但是换一种方式:

const showThis = () => {
    console.log(this);
}

var o = {
    showThis: showThis
}

showThis(); // global
o.showThis(); // global

我们看到,改成箭头函数后,不论用什么引用来调取它,都不影响this的值

class C {
    showThis() {
        console.log(this);
    }
}
var o = new C();
var showThis = o.showThis;

showThis(); // undefined
o.showThis(); // global

可以得出结论:生成器函数、异步生成器函数和异步普通函数跟普通函数行为是一致的,异步箭头函数与箭头函数行为是一致的。

this关键字的机制

函数能够弓用定义时的变量,如上文分析,函数也能记住定义时的this,因此,函数内部必定有一个机制来保存这些信息。

在JavaScript标准中,为函数规定了用来保存定义时上下文的私有属性[[Environment]。

当一个函数执行时,会创建一条新的执行环境记录, 记录的外层词法环境(outer lexicalenvironment)会被设置成函数的[Environment]。

操作this的内置函数

Function.prototype.call和Function.prototype.apply 可以指定函数调用时传入的this值

function foo(a, b, c){
    console.log(this);
    console.log(a, b, c);
}
foo.call({}, 1, 2, 3);
foo.apply({}, [1, 2, 3]);

另外,还有Function.prototype.bind 它可以生成一个绑定过的函数,这个函数的this值固定了参数

function foo(a, b, c){
    console.log(this);
    console.log(a, b, c);
}
foo.bind({}, 1, 2, 3)();

参考原文:JavaScript执行(三):你知道现在有多少种函数吗?

上一篇下一篇

猜你喜欢

热点阅读