箭头函数需要注意的地方
答案:
箭头函数有几个使用注意点。
(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对象,就是定义时所在的对象,而不是使用时所在的对象,用callapplybind也不能改变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
听风是风
听风是风
行星飞行