我们都知道函数这个高级公民在js中的地位很高。用处很大,可是却很难搞!函数内部有一个很厉害的内部属性this关键字。而bind、call、apply是函数的3个非继承方法(但是在原型链中找到了这些方法!Fuction.prototype中找到了!所以我觉得是继承自Fuction原型对象),他们的作用都是用来改变this指向的。那么this到底是什么东西?有什么作用?

了解函数中的this

this引用的是函数据以执行的环境对象。也就是说this就像函数的一个指针,会指向函数执行环境。看一个例子:

window.color = 'red'function say(){    console.log(this.color)}say() //redwindow.say() //与say() 等价与

可以看到,this指向了window对象。因为是window调用了say函数,所以say函数的执行环境widow;看下面的例子:

window.color = 'red'var obj = {    color:'yellow',    say:function(){        console.log(this.color)    }}obj.say(); //yellow//此时say方法是做为obj的方法调用,也就是说obj是say函数的执行环境对象,所以this也就指向了objvar fun = obj.say;fun() //red

将obj的say方法赋值给fun后,调用fun后发现this指向了window?也就是说函数此时的执行环境是window?
这里需要明白一点,函数名仅仅是一个包含指针的变量,函数是复杂数据类型,所以函数名就只是一个指针,指向堆中的内存地址!所以fun此时只是复制了指针地址,写成下面的写法就会看的清晰了:

window.color = 'red'function say(){    console.log(this.color)}var obj = {    color:'yellow',    say:say}obj.say(); //yellowvar fun = say;window.fun() //red

apply和call

js提供了一些可以改变函数执行作用域的方法。因为普通函数如果通过上面的写法来改变this执行时上下文,写法就太过于麻烦。

apply:
语法:apply(thisObj,数组参数)
定义:应用某一个对象的一个方法,用另一个对象替换当前对象
说明:如果参数不是数组类型的,则会报一个TypeError错误。

call方法:
语法:call(thisObj, arg1, arg2, argN)
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
apply与call的唯一区别就是接收参数的格式不同。

window.color = 'red'function say(){    console.log(this.color)}var obj = {    color:'yellow'}say.call(obj); //yellow

bind方法

bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。

var module = {  x: 42,  getX: function() {    return this.x;  }}var unboundGetX = module.getX;console.log(unboundGetX()); // The function gets invoked at the global scope// expected output: undefinedvar boundGetX = unboundGetX.bind(module);console.log(boundGetX());// expected output: 42

需要注意:

1.作为构造函数使用的绑定函数

function A(a,b){    this.a=a;    this.b=b    console.log(this)}var a = A.bind({x:1},1)var b = a(5) // {x: 1, a: 1, b: 5}function A(a,b){    this.a=a;    this.b=b    console.log(this)}var a = A.bind({x:1},1)//作为构造函数使用的绑定函数var b = new a(5) // {a: 1, b: 5}

可以看出来:使用 new 操作符去构造一个由目标函数创建的新实例。bind提供的 this 就会被忽略。不过提供的参数列表仍然会插入到构造函数调用时的参数列表之前。

2.bind的实现

Function.prototype.bind = function(context){  var args = Array.prototype.slice(arguments, 1),  F = function(){},  self = this,  bound = function(){      var innerArgs = Array.prototype.slice.call(arguments);      var finalArgs = args.concat(innerArgs);      return self.apply((this instanceof F ? this : context), finalArgs);  };  F.prototype = self.prototype;  bound.prototype = new F();  retrun bound;};

3.js中当函数执行bind后再次执行bind或call时会怎样

var test = function(x,y){    console.log(this,arguments)}var a=test.bind({s:1},1)a.call({d:1},2) // {s: 1} 1 2var b = a.bind({d:1},2)b()// {s: 1} 1 2var c = b.bind({e:3},3) c()// {s: 1} 1 2 3

es5文档中中说到如果我们在一个由bind创建的函数中调用call,假设是x.call(obj,y,z,…)并且传入this,和参数列表的时候会执行下面的步骤:
1.首先用三个参数分别保存函数x函数的内部属性中存的this值目标函数参数列表
2.然后执行目标函数的内部call函数,也就是执行目标函数的代码,并且传入1中保存的this和实参(这里的实参是目标函数本来就有的也就是bind时传入的实参加上调用call时传的实参)
重点在1中,从es5的bind函数说明中我们知道,当我们用一个函数调用bind的时候,返回的函数中会保存这三个参数。所以最后调用call的时候执行的函数是目标函数,也就是调用了bind的函数,传入的this也是bind调用时传入的,这些都是无法被修改的了,但是参数是调用bind和call时的叠加,这是我们唯一可以修改的地方。执行两次bind的原理可以参考bind的源码,和call的差不多,也是目标函数和this是被固定的了,只有参数列表会叠加。

箭头函数

箭头函数与普通函数些不同,有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

function foo() {  return () => {    return () => {      return () => {        console.log('id:', this.id);      };    };  };}var f = foo.call({id: 1});var t1 = f.call({id: 2})()(); // id: 1var t2 = f().call({id: 3})(); // id: 1var t3 = f()().call({id: 4}); // id: 1