2-es6-箭头函数

85 阅读4分钟

箭头函数需要注意的地方

答案:

箭头函数有几个使用注意点。
(1)函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。即按照作用域链往上一层层查找,注意是定义的时候就缺点了作用域的。所以箭头函数的this是在定义的时候就已经确定了this,不会再改变了。
(2)不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
(3)不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

上面四点中,第一点尤其值得注意。this 对象的指向是可变的,但是在箭头函数中,它是固定的。

function foo() {
  setTimeout(() => {
    console.log("id:", this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });
// id: 42

总结:

  • 1、箭头函数的this指向为作用域链最近的this,即上层的函数作用域的this(不是函数没有this);

  • 2、箭头函数的this指向是不可以被call或者隐式来进行改变的,即定义的时候就决定了this指向;

  • 3、obj.fn()()可以拆分为fn=obj.fn(),fn();

  • 4、obj.fn.call(obj1)()可以拆分为fn=obj.fn.call(obj1);返回的就是闭包函数,fn();

  • 定时器带functon的this指向window,事件绑定的addEvent带function的this指向dom对象

箭头函数和普通函数有什么区别

答案:

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象,用call apply bind也不能改变this指向
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
  • 箭头函数没有原型对象prototype

经典面试题:

1、
this.x = 9;  
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX(); //9 

var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

retrieveX只是getX函数的引用,也就是只是getX的一个指针(getX的另一个指针是module.getX),所以retrieveX还是指向getX函数本身的

2、
function foo() {
    console.log( this.a );
}

var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };

o.foo(); // 3
(p.foo = o.foo)(); // 2


3、
var A = function( name ){ 
    this.name = name;
};
var B = function(){ 
    A.apply(this,arguments);
//相当于执行了一次A函数,可以理解为把A的内容都写在B这函数里面
};
B.prototype.getName = function(){ 
    return this.name;
};
var b=new B('sven');
console.log( b.getName() ); // 输出:  'sven'


4、
function foo(el) {
    console.log( el, this.id );
}

var obj = {
    id: "awesome"
};

// 使用`obj`作为`this`来调用`foo(..)`
[1, 2, 3].forEach( foo, obj ); // 1 awesome  2 awesome  3 awesome


5、
function foo() {
    console.log( this.a );
}

var obj1 = {
    a: 2,
    foo: foo
};

var obj2 = {
    a: 3,
    foo: foo
};

obj1.foo(); // 2
obj2.foo(); // 3
//obj1.foo是一个方法的地址,方法.call
obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2

obj1.foo和obj2.foo就是一个指向foo函数的地址,即效果和foo.call(obj2)效果一样

注意:
new绑定的优先级高于隐含绑定(new和call/apply不能同时使用,所以new foo.call(obj1)是不允许的,也就是不能直接对比测试 new绑定 和 明确绑定)
/*非严格模式*/

var name = 'window'

var obj1 = {
    name: '听风是风',
    fn1: function () {
        console.log(this.name)
    },
    fn2: () => console.log(this.name),
    fn3: function () {
        return function () {
            console.log(this.name)
        }
    },
    fn4: function () {
        return () => console.log(this.name)
    }
}
var obj2 = {
    name: '行星飞行'
};

obj1.fn1();//听风是风
obj1.fn1.call(obj2);//行星飞行
//显示绑定(apply/bind/call)obj2大于隐式绑定window

obj1.fn2();//window
//首先箭头函数这个函数上下文没有this,所以会指向上上一层的函数作用域,又因为obj是对象不是函数,所以上一层函数作用域为window
obj1.fn2.call(obj2);//window(*)
//箭头函数的this由外部环境决定,且一旦绑定无法通过call,apply或者bind再次改变箭头函数的this,所以这里虽然使用了call方法但依旧无法修改,所以this还是指向window

obj1.fn3()();//window---闭包返回window---可以改写为var fn=obj1.fn3();fn(),这样就很明显知道this指向window
obj1.fn3().call(obj2);//行星飞行
//同样拆分为var fn=obj1.fn3();fn.call(obj2);显示绑定大于隐式绑定,就优先使用obj2的作用域,不使用隐式的window
obj1.fn3.call(obj2)();//window
//拆分为var fn=obj1.fn3.call(obj2);执行了一层返回的是闭包的那层,最后用
fn();使用默认绑定的window

obj1.fn4()();//听风是风
//var fn = obj1.fn4();window.fn();//无法改变箭头函数this
obj1.fn4().call(obj2);//听风是风(*),可以改写成这样var fn = obj1.fn4();fn.call(obj2);//显式绑定依旧无法改变this
obj1.fn4.call(obj2)();//行星飞行
//虽然无法直接改变箭头函数的this,但可以通过修改上层上下文的this达到间接修改箭头函数this的目的。代码可以改写成var fn = obj1.fn4.call(obj2);//fn4的this此时指向obj2
window.fn();//隐式绑定无法改变箭头函数this,this与fn4一样

/*非严格模式*/
var name = 'window'

function Person(name) {
  this.name = name;
  this.fn1 = function () {
    console.log(this.name);
  };
  this.fn2 = () => console.log(this.name);
  this.fn3 = function () {
    return function () {
      console.log(this.name)
    };
  };
  this.fn4 = function () {
    return () => console.log(this.name);
  };
};

var obj1 = new Person('听风是风');
console.dir(obj1);
var obj2 = new Person('行星飞行');

obj1.fn1();
obj1.fn1.call(obj2);

obj1.fn2();
obj1.fn2.call(obj2);

obj1.fn3()();
obj1.fn3().call(obj2);
obj1.fn3.call(obj2)();

obj1.fn4()();
obj1.fn4().call(obj2);
obj1.fn4.call(obj2)();


听风是风
行星飞行

听风是风
听风是风

window
行星飞行
window

听风是风
听风是风
行星飞行