一文带你全面解析JavaScript的this指针(1)

209 阅读4分钟

this指向哪里

在JavaScript中,要想完全理解this,首先要理解this的绑定规则,this的绑定规则一共有5种哦

  • 默认绑定
  • 隐式绑定
  • 隐式绑定丢失
  • 显式(硬)绑定
  • new绑定
  • ES6新增箭头函数绑定

默认绑定

默认绑定通常是指函数独立调用,不涉及其他绑定规则。非严格模式下,this指向window,严格模式下,this指向undefined。

题目1.1:非严格模式

//非严格模式
var lusin = 'shuRen';
function print () {
    this.lusin = 'lusin is lusin';
    console.log(this);
    console.log(this.lusin);
}

print(); // window shuren

//非严格模式,print()为默认绑定,this指向window,所以打印window和234 //这个lusin,其实在预编译过程中,foo和print函数会存放在全局Go中(即window对象上),所以上述代码就类似下面这样:

window.lusin = 'shuren';
function print() {
this.lusin = 'lusin is lusin';
}

//就可以理解为转换为显示调用
window.lusin();

题目1.2严格模式 把题目1.1的代码原封不动的copy下来添加"use strict"可以开启严格模式,看看严格模式下的执行结果

"use strict"
var lusin = 'shuRen';
function print () {
    this.lusin = 'lusin is lusin';
    console.log(this);
    console.log(this.lusin);
}

当javaScript开启严格模式之后默认绑定,函数内部this指向undefined,但全局对象window不会受影响 答案

undefined
Uncaught TypeErrorCannot read property 'lusin' of undefined//因为这就变成了undfined.lusin
很明显undefined不是一个对象

题目1.3 let/const

let lusin_name = 'zhoushuren';
const bajin_name = '李尧堂';
var xuzhimo_name = '章垿';

function print() {
    console.log(this.lusin_name);
    console.log(this.bajin_name);
    console.log(this.lxuzhimo_name);
}

print();
console.log(this.lusin_name);

let和const定义的变量存在暂时性死区,而且不会挂载到window 如果有想要深入了解的小伙伴可以看下这篇文章juejin.cn/post/692564… 答案

undefined
undefined
3
undefined

隐式绑定

关于隐式绑定你所要关注的一点是函数的调用是在某个对象上触发的,即调用位置存在上下文对象,通俗的说就是 xxx.func()这种调用模式。

此时的func的this指向xxx,但如果存在链式调用,例如xxx.yyy.zzz.func,记住一个原则:this永远指向最后一个调用他的对象。 题目2.1: 隐式绑定

var  lusin_name ='wanglusin';
const  famousName = {
   miaoZeDong = '毛润之';
   bajin = '巴金';
   lusin_name = '鲁迅'
   printName () {
       console.log(this.lusin_name);
   }
}

famousName.printName();

这个就是典型的xxx.func()的隐式调用的方式 此时func的this指向xxx,但如果存在链式调用,例如xxx.YYY.ZZZ.func 记住一个原则:this永远指向最后调用它的哪个对象。

答案

//鲁迅

题目2.2再来锻炼一题

var greatPeople = '鲁迅';

function printGreatPeopleName () {
console.log('this.greatPeople');
};

const greatPeopleObj = {
    lusin_name: '鲁迅';
    printGreatPeopleName: printGreatPeopleName;
}

console.log(printGreatPeopleName());

console.log(greatPeopleObj.printGreatPeoppleName());

这道题到现在是不是清楚很多啦; printGreatPeopleName()是默认绑定; greatPeopleObj.printGreatPeoppleName()是隐式绑定;

对象链式调用

var worldfamousPeople = {
     writer: '泰戈尔',
    chineseFamousPeople: {
    writer: '鲁迅',
    printFamousName: function () {
    console.log(this.writer);
    }
    }
}

console.log(this.worldfamousPeople.printFamousName());

答案

鲁迅

隐式绑定丢失

隐式绑定会出现丢失的情况

  • 使用另一个变量作为函数别名,之后使用别名执行函数
  • 将函数作为参数传递时会被隐性赋值

题目3.1取函数别名

var writer = '鲁迅';

var famousObj  = {
   writer: '李大钊',
   printFamousName: function () {
       console.log(this.writer);
   }
}


var famousAlias = famousObj.printFamousName;
console.log(famousAlias());

console.log(famousObj.printFamousName())

JavaScript的引用数据类型是将地址地址内存存入栈内存,而其真正的本体是存在堆内存当中;上面说将famousObj.printFamousName赋值给famousAlias,所以其实就相当于famousObj.printFamousName的内存地址赋值给famousAlias,所以你就可以理解为直接执行了堆内存的函数,而不像隐式调用,执行时需要先通过指针找到堆内存然后执行了。 笼统的记,只要fn前面什么都没有,肯定不是隐式绑定

如果对值类型和引用类型有疑惑的可以移步这juejin.cn/post/684490…

答案

鲁迅
李大钊

题目3.2:取函数别名 如果取函数别名这个动作不是发生在全局作用域,而是发生在对象之中,又会是怎样一个结果呢?


const famousNameObj = {
    lusin_name: '周树人',
    printName: function () {
        console.log(this.lusin_name);
    }
}

s
var lusin_name = '鲁迅';
var printName = famousNameObj.printName;

const famousNameObjOne = {
lusin_name: '豫才',
printName: famousNameObj.printName
}

console.log(famousNameObj.printName());
console.log(printName());
console.log(famousNameObjOne.printName());

famousNameObj.printName()和printName()的执行结果我就不多说了 来讲讲famousNameObjOne.printName()指向了famousNameObj.printName的堆内存,此后执行与famousNameObj无关,this指向当前所处的作用域 答案

周树人
鲁迅
豫才

题目3.3函数作为参数传递

var lusin_name = '鲁迅';
function printName () {
    console.log(this.lusin_name);
}

function printNameFactory (fn) {
console.log(this);
fn();
} 

var famousPeopleObj = {
lusin_name: '鲁迅',
printName
}

console.log(printNameFactory(famousPeopleObj.printName));

用函数预编译的知识来解答这个问题: 函数预编译四部曲前两步分别是: 1.找形参和变量声明,值赋予undefined 2.将形参和实参相统一,也就是将是实参的值赋值给赋予形参。

obj作为实参,将其值赋予给形参fn,是将famousPeopleObj.printName所指向的内存地址赋给了fn,此后fn执行就不会和famousPeopleObj产生关系,fn为默认绑定。

答案

Window
鲁迅

题目3.4函数作为参数传递

function printName () {
    console.log(this.lusin_name);
}

function  printNameFactory(fn) {
    console.log(this);
    fn()
};


var famousPeopleObj = { lusin_name: '鲁迅', printName };
var lusin_name = '鲁迅'var famousPeopleObjTwo = { lusin_name: '周树人',printNameFactory };


famousPeopleObjTwo.printNameFactory(famousPeopleObj.printName);

console.log(this); famousPeopleObjTwo.printNameFactory符合xxx.fn格式,printNameFactory为隐式绑定。this指向famousPeopleObjTwo打印{lusin_name:'周树人',printNameFactory: f} fn为默认绑定,不再赘述,还有疑问可看上题