今天是准备面试的第八天,那我们就讲一讲我们在代码中经常遇到的this关键字吧。
最重要的概念:this在函数定义的时候确定不了,只有在函数执行,this指向调用它的对象。
你把这句话理解了,你就初步懂this的指向了
一、this指向的分类
我将其分为四类
- 函数调用模式
- 方法调用模式
- 构造函数调用模式
- cell,apply,bind重指向模式
是不是感觉有点晕呀!别忙我们一个一个来讲解
二、解说this的指向
1.函数调用模式
当一个函数不是一个对象的属性时,当它被当做一个函数来调用,this绑定为全局对象,及最外层的环境windows。
function fn(){
var a = 'hello';
console.log(this.a);//undefined
console.log(this);//window
}
fn();
这里的this指向的是window
2.方法调用模式
当一个函数是一个对象的属性,它就可以被称为该对象的一个方法,当调用这个对象的方法时,this就绑定的就是这个对象
let obj = {
name: 'world',
fn: function(){
console.log(this.name);
}
}
obj.fn()
这时我就有一个大胆的猜想当函数为一个对象的属性时,this指向的是包含他且离它最近的一个对象
let obj = {
name: 'world',
b: {
fn: function(){
console.log(this.name);//undefined
}
}
}
obj.b.fn()
let obj = {
name: 'world',
b: {
name: 'home',
c:{
fn: function(){
console.log(this.name);//undefined
}
}
}
}
obj.b.c.fn()
从这些结果,我们可以得出当函数为一个对象的属性时,this指向的是包含它且离它最近的一个对象
我们来加深一下对结论一的理解,我们稍微改一下上一个代码
let obj = {
name: 'world',
b: {
name: 'home',
c:{
fn: function(){
console.log(this.name);//window
}
}
}
}
let t = obj.b.c.fn;
t();
3.构造函数调用模式
如果在调用函数时,前面用了new这个关键词来修饰,那么就创建了一个连接该函数prototype的对象,同时this就会绑定到该函数的对象上。
- 没有返回值
当new的方法没有返回值,this指向的是new的那个对象
function fn(){
console.log(this);//fn()
}
let a = new fn();
- 有返回值,但返回值不是对象
当new的方法有返回值,但返回值不是对象,this依然指向的是new的那个对象(例外:如果返回值是null,this指向的是new的那个对象)
function fn(){
this.name = 'Tom'
return function(){
};
}
let a = new fn();
console.log(a.name)//Tom
- 有返回值,但返回值是引用数据类型(对象,数组。。。)
this指向的是return返回的那个对象
function fn(){
this.name = 'Tom';
}
let a = new fn();
console.log(a.name)//Tom
function fn(){
this.name = 'Tom';
return [];
}
let a = new fn();
console.log(a.name)//undefine
function fn(){
this.name = 'Tom';
return {
name: 'Zoo'
};
}
let a = new fn();
console.log(a.name)//Zoo
4. call,apply,bind重指向模式
如果要详细讲解这三的用法,篇幅可能会有点长,我就简单的看一下他们的用法吧!
call(obj, param1, param2)
apply(obj, [param1, param2])
bind(obj, param1, param2)()
第一个参数必填
直接上代码
var name = 'Tom';
var o = {
name: 'cat'
};
function sayName(){
console.log(this.name);
}
sayName();//window
sayName.apply(o);//cat
sayName.call(o);//cat
很多源码使用apply就是用来传入数组参数(也可以说成去掉数组两边的中括号)。
三、练手题
我在网上找了三个练手题,看你是不是真的懂了
- 第一个
第一个,使用的是:方法调用模式——没有返回值的,this指向的是person1,第二个使用的是:函数调用模式,this指向的是window - 第二个
javascript中的一个思想:万物皆对象,new B(),new C(),都是new了一个没有返回值的方法,this指向的是new出来的那个对象,this.n = 9999的意思是给new的这个B对象加了一个n:9999的属性,但是第二个var n = 8888,它只是函数内部的环境(不在作用域链上作用域链在四种会简单提一下),c.n是找不到n=8888的,只能找到该环境外层的原型中的4400。 - 第三个
这个应该就比较简单了,第一个输出就是使用的函数调用模式,第二个就是方法调用模式——无返回值
四、(拓展方向)this存在是为了解决什么问题呢?
想明白this存在是为了解决什么问题,怎么也绕不开作用域链这个问题(不知道的可以去查查作用域链的资料),一个参数能识别该作用域向外到全局执行环境(window)遇到的第一个同名变量,所以与该参数同级的参数是无法在该作用域链找到的,为了解决这个问题就出了this这个关键字。
那我们看代码理解一下
let name = 'cat';
var obj = {
name: 'Tom',
fn: function(){
console.log(name);//cat
}
}
obj.fn();
可能我没说清楚,我只是给你提供了更加深入的方向
学习,最后都要回归书本,书本才是最好的老师,多看相关的书籍,你收获颇多