箭头函数的 this 问题
- 箭头函数没有
prototype
(原型),所以箭头函数本身没有this;
const fn = () => {};
console.log(fn.prototype); //undefined
- 箭头函数的
this
指向定义时的外层第一个普通函数,且普通函数的this
指向改变箭头函数的也会随之改变;
let box,
Lbox = { msg: 'Lfn的this指向' },
Zbox = { msg: 'Zfn的this指向' };
function Zfn() {
box(); // 结果:{ msg: 'bar的this指向' }
}
function Lfn() {
box = () => {
console.log(this, 'this指向定义的时候外层第一个普通函数');
};
}
Lfn.call(Lbox); // 将Lfn的this指向Lbox
Zfn.call(Zbox); // 将Zfn的this指向Zbox,但不会改变box中的this指向
3.改变箭头函数的this
指向会失败,但不会报错;
普通函数的
this
在非严格模式下回指向window
,严格模式下会指向undefined
;
如箭头函数外层没有普通函数包裹,不论在严格还是非严格模式下都指向window
;
严格模式必须在开头声明,中途声明无效;
4.箭头函数的参数arguments
问题;
箭头函数的
this
指向普通函数时,arguments
继承this
指向的普通函数中的参数;
箭头函数的this
指向全局对象的时候,arguments
会报未声明的错误,可以使用ES6的rest
参数(...拓展符);
let fn = (first, ...args) => {
console.log(first, args); // 1 [2, 3, 4]
};
a(1, 2, 3, 4);
args是真数组,可以用数组的方法;arguments
是类数组,使用数组方法需提前转化(拓展运算符或者Array.from()进行转化)
- 箭头函数没有
constructor
,不论this
指向哪里,都不能用new
关键字调用;
let fn = () => {};
let Lbxin = new fn(); // Lbxin is not a constructor
- 箭头函数不支持
new.target
new.target
是ES6新增的语法,用于确定构造函数是否为new
调用的;
普通函数new调用会返回该函数的引用,箭头函数调用时需根据this
指向进行判断,当指向全局对象时,new.target
会报错,当指向普通函数时会返回普通函数的引用。
- 箭头函数没有
constructor
,不能通过new
关键字调用,所以也没有new.target
值;也没有构建原型的需求,所以箭头函数上没有prototype
这个属性;也不能通过super
关键字访问原型的属性;不过和this、arguments、new.target
一样,可以由外围最近一层的普通函数进行决定;
var Foo = () => {};
console.log(Foo.prototype); // undefined
- 箭头函数不会创建在自己的
this
,只会从自己的作用域链的上一次继承this
,上一层也是继承自普通函数;
const obj = {
a: () => {
console.log(this) //window
d = () => {console.log(this)} //window 作用域链上无普通函数
d()
},
b: {
c: () => {console.log(this)} //window
}
}
obj.a()
obj.b.c()
const obj = {
a: function() {
console.log(this) //obj
d = () => {console.log(this)}
//obj 继承自原型链上最近的obj.a普通函数中的this,即对象obj
d()
},
b: {
c: () => {console.log(this)} //window
}
}
obj.a()
obj.b.c()
闭包函数的定义基于外部函数的执行;
相对于变量的取值是取决于函数的定义环境,而this的取值正好相反,是取决于函数运行时的上下文;
window.setTimeout()和window.setInterval()的函数中的this默认是window对象
变量提升中,函数声明比变量提升优先级高,变量提升会先提为undefined,函数则是引用提升,后续代码执行时再进行相应的复制操作;
动态作用域的作用域是基于调用栈的,而静态作用域的则是在定义的时候就确定了的;JS的词法作用域是静态作用域;
普通函数通过
new
调用时,会先执行construct
方法,创建一个实例对象,然后再执行函数体,将this
绑定到该实例对象上;当直接执行的时候会先执行call
方法,直接执行函数体,此方法会有函数调用语法糖的效果;
内部的函数存在外部作用域的引用就会形成闭包;闭包中的变量存储的位置是堆内存;假如闭包中的变量存储在栈内存中,那么栈内存的回收会把处于栈顶的变量自动回收,所以闭包中的变量如果存储于栈中那么栈内存中的变量被销毁后闭包中的变量就消失了,所以闭包中的变量是存储在堆内存中的。
// 节流
function throttle(fn, timeout) {
let timer = null
return function (...arg) {
if(timer) return
timer = setTimeout(() => {
fn.apply(this, arg)
timer = null
}, timeout)
}
}
// 防抖
function debounce(fn, timeout){
let timer = null
return function(...arg){
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, arg)
}, timeout)
}
}
函数在哪里创建的,函数的上级作用域就是谁;
全局下的栈内存只在页面关闭的时候会被释放,另外函数私有作用域下的内弄被外部引用时,栈内存就不能释放里面的基本值,也就不能释放该栈内存;一般情况下函数执行完毕后函数的私有作用域就会被释放掉。