我正在参加「掘金·启航计划」
函数中的this
- 函数调用时,JavaScript或默认给this绑定一个值
- this的绑定与定义的位置没有关系
- this的绑定与调用方式以及调用的位置有关系
- this是在运行时被绑定的
1.this的绑定规则
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
1.1默认绑定
- 普通函数被独立调用
function foo() {
console.log(this);
}
foo(); // windows
- 函数定义在对象中,但是被独立调用
var obj = {
bar: function(){
console.log(this);
}
}
let baz = obj.bar;
baz(); // window
- 严格模式下,独立调用的函数this指向undefined
1.2 隐式绑定
通过某个对象进行调用
也就是函数调用位置是某个对象发起的函数调用。
function foo() {
console.log(this);
};
let obj = {
baz: foo,
};
obj.baz(); // obj
隐式绑定有一个前提条件
- 必须在调用对象内部有一个对函数的引用(比如一个属性)
- 如果没有这样的引用,在调用的时候,就会报找不到该函数的错误
- 通过这个引用,间接将this绑定到了这个对象上
1.3 通过new绑定
new会经过以下几个阶段:
- 创建新的空对象
- 将this指向这个空对象
- 执行函数体重的代码
- 没有显示返回非空对象时,默认返回这个对象
1.4 显示绑定
如果我们不希望在对象内部包含这个函数的引用,同时又希望在该函数上进行强制调用该函数。
我们可以使用apply与call方法
- 这两个方法第一个参数都是为一个对象
- 这个对象就是给this准备IDE
- 在调用你这个函数时,会将this绑定到传入的这个对象身上
- 后面的参数:apply为数组,call为参数列表
比如:
foo.aply(obj,["as",12,4]);
foo.call(obj,"as",12,4);
如果想要将一个函数总是显示绑定到一个对象上,我们可以使用bind方法。
- bind方法,bind()方法创建一个新的绑定函数
foo.bind(obj);
bind函数传入参数的方式与call是一样的。
1.5 内置函数的this绑定
-
定时器
定时器的this绑定为window。
-
事件监听
-
forEach
forEach默认绑定为window,你也可以在forEach第二个参数中传入要绑定的this对象。
var names = ["avx","asd","fgd"];
names.forEach(function(){
console.log(this);
},obj)
1.6 几种绑定规则的优先级
- 默认规则的优先级最低
- 显示绑定的优先级高于隐式绑定
- new绑定高于隐式绑定
- new不可以appLy/call一起使用
- new的优先级高于bind
- bind的优先级高于apply/call
new > bind > apply/call > 隐式绑定 > 默认绑定
2.this规则之外-忽略显示绑定
- 如果在显示绑定中,传入一个null或者undefined,那么该这个显示绑定失效,使用默认规则
- 创建一个函数的间接引用,这种情况使用默认绑定规则。
3.箭头函数
箭头函数是ES6之后新增的一种编写函数的方法。
- 箭头函数不会绑定this。arguments属性
- 箭头函数不能作为构造函数䣂使用(不能和new一起使用,报错)
3.1 箭头函数的写法
(参数列表) => {
函数体
}
3.2 箭头函数的编写优化(简写)
- 如果箭头函数只有一个参数,那么小括号可以省略
item => {
console.log(item);
}
-
如果函数体只有一行代码,可以省略大括号
并且这行代码的返回值会作为整个函数的返回值
item => consloe.log(item);
注意
这一行代码中不能包含return关键字
如果要返回值,那么这行代码的运算结果就会作为整个函数的返回值返回。
item => item*2;
- 如果默认返回值是一个对象,那么这个对象必须加()
foo = () => ({name:"hi"});
3.3箭头函数里面的this
在普通函数里面是有this标识符的,但是在箭头函数里面没有this。
通过apply来调用还是没有this指向。
查找this的规则与查找变量一样:该作用域没有this,就往上一层作用域查找,直到查找到this。
4.面试题
4.1 面试题1
第一个sss()调用时默认调用,所以this为默认绑定,this指向window,所以最后的值为window。
第二个函数调用为隐式绑定,所以this指向person,所以最后结果为person。
第三个同样也是隐式绑定,所以结果一样。
第四个是间接函数引用,返回结果为一个独立的函数,this指向window,结果为window。
4.2 面试题2
- 22行: 隐式绑定,this指向person1,结果person1
- 23行:显式绑定,this指向person2,结果为person2
- 25行:箭头函数没有this,去上层作用域查找,所以this绑定为window,最后结果为window
- 26行:箭头函数没有this,去上层作用域查找,所以this绑定为window,最后结果为window
- 28行:首先执行person1.foo3(),这个函数结果为一个函数,后面接着执行返回的这个函数,所以为函数单独调用,为默认绑定,this指向window,所以最后结果为window。
- 29行:虽然foo3()的this绑定为person2,但是后面返回的一个函数,还是相当于是函数独立调用,所以最后this还是指向window,结果为window。
- 30行:最后调用的函数为显式绑定,this指向person2,所以最后结果为person2。
- 32行:因为foo4()函数返回的结果函数没有this指向,所以会往上层作用域查找,最后this指向person1,结果为person1
- 33行:foo4()的this被显式绑定为person2,所以返回结果箭头函数this也指向person2,结果为person2.
- 34行:因为箭头函数没有this,所以显式绑定不起作用,所以最后结果为person1。
4.3 面试题3
new执行步骤:
new操作符运行示意图:
- 22行: 隐式绑定,结果为person1。
- 23行:显式绑定到person2,结果为person2。
- 25行:上层作用域查找,查找到person1
- 26行:箭头函数没有this,显式绑定失效,所以还是person1
- 28行:独立函数调用,默认绑定,window
- 29行:还是独立函数调用,默认绑定,结果为window
- 30行:显式绑定为person2,结果为person2
- 32行:箭头函数没有this,往上层查找,所以为foo4()的this,foo4()this指向person1,所以结果为person1
- 33行:foo4()this显式绑定为person2,所以最后结果为person2
- 34行:箭头函数没有this,显式绑定失效,所以结果还是为person1