昨天做试题的时候遇到了这个题目
var a = 1;function fn1() {console.log(this.a)}const fn2 = () => {console.log(this.a)}const obj = {a: 10,fn1: fn1,fn2: fn2}fn1()fn2()obj.fn1()obj.fn2()
哦这该死的网易,怎么出这么简单的题目,答案是:1,1,10,10 对,应该是这样的,吧?
可是再仔细一想,不对啊,我记得箭头函数的this对象好像不是在运行时基于函数的执行环境绑定的,他是有点奇怪的,事后仔细查了资料才知道,原来正确答案应该是 :1,1,10,1
一言概之,是因为:箭头函数的this和arguments只会从它自己所在作用域链的上一层继承,不会创建自己的this和argumrnts!!!(只有函数才会有this和arguments属性)
让我再举几个例子给泥们looklook:
//普通函数var obj = {bar: function () {console.log(this); // 被obj调用,this指向{bar: ƒ}bar: ƒ ()__proto__: Object var x = function() {console.log(this)//Window对象};return x;//闭包,this通常(即不使用箭头函数的情况)默认为全局对象,若在严格模式则为undefined}};var fn = obj.bar();fn() ; //箭头函数
// 创建一个含有bar方法的obj对象,// bar返回一个函数,// 这个函数返回this,// 这个返回的函数是以箭头函数创建的,// 所以它的this被永久绑定到了它外层函数的this。// bar的值可以在调用中设置,这反过来又设置了返回函数的值。var obj = {bar: function () {console.log(this); // {bar: ƒ}bar: ƒ ()__proto__: Object var x = (() => this);return x;}};// 作为obj对象的一个方法来调用bar,把它的this绑定到obj。// 将返回的函数的引用赋值给fn。var fn = obj.bar();//此时外层函数已经运行完毕,他的this已经绑定到{bar: ƒ}这个环境console.log(fn() === obj); // trueconsole.log(fn()); // {bar: ƒ}bar: ƒ ()__proto__: Object // 直接调用fn而不设置this,// 通常(即不使用箭头函数的情况)默认为全局对象// 若在严格模式则为undefined// 但是注意,如果你只是引用obj的方法,// 而没有调用它,this没有及时绑定到{bar: ƒ}这个环境var fn2 = obj.bar;// 那么调用箭头函数后,this指向window,因为它从 bar 继承了this。console.log(fn2()() == window); // true/* 此时等于在全局环境中调用 function () {console.log(this); // {bar: ƒ}bar: ƒ ()__proto__: Object var x = (() => this);return x;}*/
再来一粒,这次带有arguments
//普通函数var name = "The Window";var object = {name: "My Object",getNameFunc: function () {console.log(arguments[0],'aa')//4console.log(this.name)//"My Object"return function () {//闭包,this通常指向windowconsole.log(arguments[0],'a')//5return this.name;};}};console.log(object.getNameFunc('4')('5')); // The Window//箭头函数var name = "The Window";var object = {name : "My Object",getNameFunc : function(a) {console.log(this.name)//"My Object"console.log(arguments[0],'aa')//4return (a) => {console.log(arguments[0],'a')//4,这里是继承了上一层的argumentsreturn this.name; //虽然是闭包,但成功继承了上一层的this};}};console.log(object.getNameFunc('4')('5')); //"The Object"//函数都是箭头函数var name = "The Window";var object = {name : "My Object",getNameFunc : (a) => {console.log(this.name)//"The Window"//console.log(arguments[0],'aa')//errorreturn (a) => {//console.log(arguments[0],'a')//errorreturn this.name; };}};console.log(object.getNameFunc('4')('5')); //"The Window"//此时虽然是object调用了getNameFunc函数,但是箭头函数的this是指向上一层环境,即全局环境,所以this指向Window对象,而Window对象没有arguments,所以会出错
箭头函数还有一个值得注意的特性—— 无法使用call或者apply绑定this值。
由于 箭头函数没有自己的this指针,通过 call()
或 apply()
方法调用一个函数时,只能传递参数(不能绑定this),他们的第一个参数会被忽略。(这种现象对于bind方法同样成立)
var name = "The Window";var object = {name : "My Object",getNameFunc : function(a) {console.log(this.name)//"outer"console.log(arguments[0],'aa')//4return (a) => {console.log(arguments[0],'a')//4return this.name; };}};console.log(object.getNameFunc.call({name:'outer'},'4').call({name:'inner'},'5')); //"outer"
再来一题,也是网易的
function fun() {return () => {return () => {return () => {console.log(this.name)}}}}var f = fun.call({ name: 'foo' });
f.call({name:'1'})()()//foo
f().call({name:'2'})()//foo
f()().call({name:'3'})//foo
你做对了吗?
推荐阅读
this 指向详细解析(箭头函数)
箭头函数
this
JavaScript中的匿名函数及函数的闭包
彻底理解js中this的指向,不必硬背。
对匿名函数的深入理解(彻底版)