调用函数的两种方式
js中有两个不同的内部方法:[[Call]] 和 [[Construct]]。
[[Construct]]方法
当通过new关键字调用函数时,执行的是[[Construct]]方法,具有[[Construct]]方法的函数统称为构造函数。
- 创建一个新对象(new Object())
- 将函数的作用域赋给新对象(将this指针指向这个新对象)、
- 执行构造函数中的代码 (为新对象添加属性)
- 返回新对象
[[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 。")
}
}
箭头函数
箭头函数:参数 + => + 函数体 (() => {})
箭头函数和传统函数的区别
箭头函数的设计初衷就是 即用即弃 ,不能用它定义新的类型,所以会缺少很多属性和方法。
- 没有 this 绑定,必须通过查找作用域链决定其值,所以其值由外围最近一层的非箭头函数决定。且不可以通过 call/apply/bind 改变 this 的值。
- 不能通过 new 关键字调用,箭头函数没有 [[Construct]] 方法
- 没有原型,不存在 prototype 属性
- 没有 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