深入理解ES6-3.函数(2)

25 阅读2分钟

调用函数的两种方式

js中有两个不同的内部方法:[[Call]] 和 [[Construct]]。

[[Construct]]方法

当通过new关键字调用函数时,执行的是[[Construct]]方法,具有[[Construct]]方法的函数统称为构造函数。

  1. 创建一个新对象(new Object())
  2. 将函数的作用域赋给新对象(将this指针指向这个新对象)、
  3. 执行构造函数中的代码 (为新对象添加属性)
  4. 返回新对象

[[Call]]方法

直接执行代码中的函数体的,执行的是[[Call]]方法。

判断函数调用的方式

在es5中,想要判断一个方法是否由new关键字调用,我们可以在方法里通过 this instanceof [className] 的形式判断this的值是否为构造函数的实例,但是通过call调用的函数,也能改变其this指向。

所以在es6中提供了 元属性new.target,当调用函数的[[Construct]]方法时,new.target 被赋值为新创建的对象实例,当调用函数的[[Call]]方法时,new.target 的值为 undefined。

function Person(name) {
    // 也可以直接判断某种特定类型 typeof new.target === Person
    if (typeof new.target !== "undefined") {
        this.name = name;
    } else {
        throw new Error("必须通过new 关键字来调用Person 。")
    }
}

箭头函数

箭头函数:参数 + => + 函数体 (() => {})

箭头函数和传统函数的区别

箭头函数的设计初衷就是 即用即弃 ,不能用它定义新的类型,所以会缺少很多属性和方法。

  1. 没有 this 绑定,必须通过查找作用域链决定其值,所以其值由外围最近一层的非箭头函数决定。且不可以通过 call/apply/bind 改变 this 的值。
  2. 不能通过 new 关键字调用,箭头函数没有 [[Construct]] 方法
  3. 没有原型,不存在 prototype 属性
  4. 没有 arguments/super/new.target 绑定,这些值由外围最近一层的非箭头函数决定

箭头函数没有this绑定

谁调用它,this就指向谁,所以对于 F 函数,它是在 global 的环境上被调用的,所以在指向F() 时,找不到 this.doSomething 会报错

let Obj = {
    id: "123456",
    init: function () {
        return function handler() {
            this.doSomething('aa')
        }
    },
    doSomething: function (type) {
        console.log(type, this.id);
    }
};
let F = Obj.init();
F()     // Uncaught TypeError: this.doSomething is not a function

我们可以通过 bind 改变方法的 this 绑定,将 handler 方法的 this 指向绑定到 Obj 对象上。

let Obj = {
    id: "123456",
    init: function () {
        return (function handler() {
            this.doSomething('aa')
        }).bind(this)
    },
    doSomething: function (type) {
        console.log(type, this.id);
    }
};
let F = Obj.init();
F()     // aa 123456

但是,使用箭头函数的this由其最近一层的非箭头函数决定,所以我们也可以采用箭头函数的方式达到this的指向指向Obj。

let Obj = {
    id: "123456",
    init: function () {
        return () => {
            this.doSomething('aa')
        }
    },
    doSomething: function (type) {
        console.log(type, this.id);
    }
};
let F = Obj.init();
F()        // aa 123456