# 为什么需要this?
没有this,会让编写代码变得很不方便。
# this的指向
# 在全局作用域中
- 在浏览器环境中,this指向window
- 在node环境中,this指向一个空对象
# 在函数中
在执行上下文中记录着函数的调用栈、AO对象等;
this 也是其中的一条记录。
在函数执行时才动态绑定this,所以this 指向什么跟函数所处位置没有关系,而跟函数被调用的方式有关。
# this的绑定规则
# 默认绑定
函数被调用时,没有绑定任何对象。只是独立的函数调用。
这时 this 指向 window
# 隐式绑定
函数是通过某个对象进行调用的,object对象会被js引擎绑定到fn函数中的this里面。this 指向的就是当前的对象。
# 显示绑定
使用 call apply bind可以指定 this 的绑定对象,可以很明确的绑定this,这个规则称之为显示绑定。
直接独立调用和call/apply/bind调用的不同在于 this 绑定的不同,
# call、apply和bind的不同
call和apply传参的方式不同,call是剩余参数
call(thisArg,item1,item2)
apply是通过数组
apply(thisArg,[item1,item2])
bind可以将函数绑定this对象,并返回一个函数
function foo(){
console.log(this)
}
var newFoo = foo.bind("aaa")
newFoo() //this指向aaa
2
3
4
5
6
# new绑定
JavaScript中的函数可以当作一个类的构造函数来使用,也就是使用new关键字。
通过一个new关键字调用一个函数时(构造器),这个时候this是在调用这个构造器创建出来的对象。
this 指向的就是创建出来的对象
function Person(name,age){
this.name = name
this.age = age
}
var okarin = new Person("okarin",19)
console.log(okarin.name,okarin.age)
2
3
4
5
6
7
8
# 内置函数的this绑定
# setTimeout
setTimeout(function(){
console.log(this) //window
},2000)
2
3
setTimeout 内部是独立的调用函数,所以在setTimeout内部普通函数的this指向的是window。
# onclick
const boxDiv =document.querySelector('.box')
boxDiv.onclick = function(){
console.log(this)
}
2
3
4
在这里this会指向box本身,所以内部是一个类似的隐式绑定。将foo绑定到了对象内部的一个属性。
# 数组中的高阶函数
var names = ['abc','cba','nba']
names.forEach(function(item){
console.log(item,this) //window
})
2
3
4
在不传递this参数的情况下,this指向window。
而数组中的高阶函数支持传递一个参数 ( thisArg ) 来指定this。
# 规则优先级
# 默认规则的优先级最低
默认规则的优先级是最低的,因为存在其他规则时,会通过其他规则的方式来绑定this.
# 显示绑定高于隐式绑定
var obj = {
name:'abc',
foo:function(){
console.log(this)
}
}
obj.foo() //obj
obj.foo.call("aaa")//aaa
2
3
4
5
6
7
8
call 和 apply 以及 bind 的优先级是高于隐式绑定的。
# new绑定的优先级高于隐式绑定
var obj = {
name:'obj',
foo:function foo() {
console.log(this)
}
}
var f = new obj.foo()//foo {}
2
3
4
5
6
7
8
指向的是函数创建的对象,所以new绑定的优先级高于隐式绑定。
# new绑定的优先级高于显示绑定
function foo() {
console.log(this)
}
var bar = foo.bind("aaa")
bar() // "aaa"
var obj = new bar() //foo{}
2
3
4
5
6
7
总结:new绑定 > 显示绑定(apply/call/bind)>隐式绑定( obj.foo() )>默认绑定(独立函数调用)
# this规则之外
# 忽略显示绑定
function foo() {
console.log(this)
}
foo.apply("aaa") //aaa
foo.apply(null) //window
foo.apply(undefined)//window
2
3
4
5
6
7
当我们使用显示绑定传入的是 null 或 undefind 时,函数内部会自动将this绑定成全局对象。
# 间接函数引用
var obj1 = {
name:'obj1',
foo:function foo() {
console.log(this)
}
}
var obj2 = {
name: "obj2"
}
// obj2.bar = obj1.foo
// obj2.bar()
;(obj2.bar = obj1.foo)() //window
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# this在箭头函数
# 箭头函数
箭头函数是ES6之后增加的一种编写函数的方法,并且比函数表达式更加简洁。
- 箭头函数不会绑定this、argment属性。
- 箭头函数不能作为构造函数来使用(不能和new一起来使用,会抛出错误)
() => {}
var foo = () => {}
var nums = [10,20,45,78]
nums.forEach(()=>{
})
2
3
4
5
6
7
8
# 箭头函数的简写
如果参数只有一个:小括号可以省略
nums.forEach(item =>{
console.log(item)
})
2
3
函数执行体只有一行代码:大括号可以省略,并且会把这行代码的执行结果作为返回值。
nums.filter(item => item % 2 === 0 )
只有一行代码并且返回一个对象:在对象外面添加一个小括号
var bar = () => ({name:'okarin',age:18})
# 箭头函数的this
箭头函数不使用this的(也就是不绑定this),而是根据外层作用域来决定this。
一个模拟网络请求的案例:
- 这里使用setTimeout来模拟网络请求,请求到数据后如何可以存放到data中呢?
- 需要拿到obj对象,设置data;
- 但是直接拿到的this是window,需要在外层定义:
var _this = this
- 在setTimeout的回调函数中使用_this就代表了obj对象
var obj = {
data:[],
getData:function(){
var _this = this
setTimeout(function () {
var result = ['abc','cba','nba']
_this.data = result
},200);
}
}
obj.getData()
2
3
4
5
6
7
8
9
10
11
12
13
上面的代码在ES6之前是我们最常用的方式,从ES6开始,使用箭头函数:
- 为什么在setTimeout的回调函数中可以直接使用this呢?
- 因为箭头函数并不绑定this对象,那么this引用就会从上层作用域中找到对应的this
var obj = {
data:[],
getData:function(){
setTimeout(()=> {
var result = ['abc','cba','nba']
this.data = result
},200);
}
}
obj.getData()
2
3
4
5
6
7
8
9
10
11
思考:如果getData也是一个箭头函数,那么setTimeout中的回调函数中的this指向谁呢?
- 答案是window;
- 依然是不断的从上层作用域找,那么找到了全局作用域;
- 在全局作用域内,this代表的就是window
var obj = {
data: [],
getData: () => {
setTimeout(() => {
console.log(this); // window
}, 1000);
}
}
obj.getData();
2
3
4
5
6
7
8
9
10
# this面试题
- 面试题一
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
sss(); //独立调用 指向 window
person.sayName(); //隐式调用 指向 person
(person.sayName)(); //隐式调用 指向 person
(b = person.sayName)(); //间接函数引用 指向window
}
sayName();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 面试题二
var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var person2 = { name: 'person2' }
person1.foo1(); //person1 隐式绑定
person1.foo1.call(person2); //person2 显示绑定优先级大于隐式绑定
person1.foo2(); //window 这里的foo2是一个箭头函数,对象里面的箭头函数的上层作用域不是对象,在此处是全局作用域
person1.foo2.call(person2);//window 箭头函数不遵循优先级规则
person1.foo3()();//window foo3()会返回一个函数,对函数的直接调用是独立函数调用
person1.foo3.call(person2)();//window 在这里外层函数的this是person2 但是最后是一个独立函数调用
person1.foo3().call(person2);//person2 这里是显示绑定
person1.foo4()();//person1 箭头函数不绑定this 上层作用域是隐式绑定的person1
person1.foo4.call(person2)(); //person2 foo4上层作用域是显示绑定的person2
person1.foo4().call(person2); //person1 this不遵循规则 上层作用域是隐式绑定的person1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
- 面试题三
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
},
this.foo2 = () => console.log(this.name),
this.foo3 = function () {
return function () {
console.log(this.name)
}
},
this.foo4 = function () {
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1() //person1
person1.foo1.call(person2) //person2
person1.foo2() //person1 这里的箭头函数的上层作用域是一个函数,所以this是person1
person1.foo2.call(person2) //person1 箭头函数call无效
person1.foo3()() //独立调用 window
person1.foo3.call(person2)() //window 独立调用
person1.foo3().call(person2) // person2 这里是显示绑定
person1.foo4()() //箭头函数上层作用域是foo4 是person1
person1.foo4.call(person2)()//person2 foo4上层作用域是显示绑定的person2
person1.foo4().call(person2) //person1 this不遵循规则 上层作用域是隐式绑定的person1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- 面试题四
var name = 'window'
function Person (name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name)
}
},
foo2: function () {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()() //window 独立函数调用
person1.obj.foo1.call(person2)()//window 独立函数调用
person1.obj.foo1().call(person2)//person2
person1.obj.foo2()()//obj 上层函数的this是obj
person1.obj.foo2.call(person2)()//person2
person1.obj.foo2().call(person2)//obj this不遵循规则 上层作用域是隐式绑定的obj
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30