理解js中的函数调用和this
概述
这是我看typescript的时候看引用资源看到的,原文在这里:Understanding JavaScript Function Invocation and "this",我简单地总结一下记下来供以后开发时参考,相信对其他人也有用。
机制
js中的函数调用机制是这样的:
- 建立一个表argList,从索引1开始塞入函数的参数。
- 表的索引0的值是thisValue。
- 把this赋给thisValue,然后调用func.call(argList)。
说这么多,其实就是想说明,函数调用f(x,y)其实就是f.call(this, x, y)的语法糖。内部是通过f.call(this, x, y)来调用的。
可以看到,虽然f(x,y)里面没有this,但是f.call(this, x, y)里面有this,所以非常好理解为什么函数调用中会有this了。
那么this是从哪里来的呢?当f(x,y)没有调用者的时候,this自动被赋值为window;当f(x,y)有调用者的时候,this被赋值为指向调用者。
例子
举几个实例感受一下:
//例子1
function hello(thing) {
console.log(this + " says hello " + thing);
}
hello.call("Yehuda", "world"); //输出Yehuda says hello world
//例子2
function hello(thing) {
console.log(this + " says hello " + thing);
}
//相当于hello.call(window, "world");
hello("world"); // 输出[object Window] says hello world
//例子3
function hello(thing) {
console.log(this + " says hello " + thing);
}
let person = { name: "Brendan Eich" };
person.hello = hello;
//相当于hello.call(person, "world");
person.hello("world"); //输出[object Object] says hello world
注意:我们这里不是strict mode。
更优雅的写法
有时候,我们希望函数没有调用者,但是this却不指向window对象。有以下2种优雅的解决方案:
箭头函数
es6里面定义了箭头函数,不只是为了简化函数的写法,而且还有这么一条有用的规定:箭头函数的this不会随调用者的不同而变化,它的this永远是被定义的函数域中的this。
//不用箭头函数
let person = {
hello: function(thing) {
return function() {
console.log(this + " says hello " + thing);
}
}
}
let helloTest = person.hello('world');
helloTest(); //输出[object Window] says hello world
//使用箭头函数
let person = {
hello: function(thing) {
return () => {
console.log(this + " says hello " + thing);
}
}
}
let helloTest = person.hello('world');
helloTest(); //输出[object Object] says hello world
需要注意的是,需要用一个function()把箭头函数包裹起来,因为如果不这样的话,它被定义的函数域是window。
bind
用箭头函数有点麻烦,我们可以这么写一个bind函数达到效果。
let person = {
hello: function(thing) {
console.log(this + " says hello " + thing);
}
}
let bind = function(func, thisValue) {
return function() {
return func.apply(thisValue, arguments)
}
}
let boundHello = bind(person.hello, person);
boundHello('world'); //输出[object Object] says hello world
es5给所有Function封装了上面的bind方法,所以我们只需要这么写:
let person = {
hello: function(thing) {
console.log(this + " says hello " + thing);
}
}
let boundHello = person.hello.bind(person);
boundHello('world'); //输出[object Object] says hello world
这就是bing()方法的来历0.0