专栏首页达达前端【面试需要】掌握JavaScript中的this,call,apply的原理

【面试需要】掌握JavaScript中的this,call,apply的原理

作者 | Jeskson

掘金 | https://juejin.im/user/5a16e1f3f265da43128096cb

2020年01月10日

前言,为什么要学习在掌握JavaScript中的this,call,apply,因为面试官会问啊!所以我们 必须掌握才能答啊!那么this是什么,Function.prototype.call和

Function.prototype.apply这两个方法又是如何使用在JavaScript中的呢。

学习掌握this是必须的,我们常常在编写JavaScript中的代码时,会常用到它。

this的指针作用域,在全局环境中执行this,表示Global对象,在浏览器中表示window对象;当通过new运算符来调用函数时,函数被当做为一个构造函数,this的指向构造函数创建出来的对象;当在函数的执行环境中使用this时,情况有些不同,如函数没有作为一个非window对象的属性,那么只是定义了在这个函数,不管这个函数是不是定义在另一个函数中,其函数中的this仍表示为window对象;如果函数表示作为一个非window对象的属性,则表示函数中的this就代表为 这个对象。

如截图情况下,在全局执行环境中使用this,表示Global对象,在浏览器中表示为window对象。

functionA(){//在A函数中定义一个B函数functionB(){console.log(this);//Windowconsole.log(typeofthis);//objectconsole.log(this===window);//true}//在A函数内部调用B函数B();}//调用A函数A();

在函数执行环境中使用this时,如果函数没有明显的作为非window对象的属性,而只是定义了函数,不管这个函数是不是定义在另一个函数中,这个函数中的this仍然表示window对象。

//定义一个对象obj,添加属性name,添加方法objFunvarobj = {name:'dada',objFun:function(){console.log(this);// Object {name: "dada"}console.log(typeofthis);//objectconsole.log(this===window);//falseconsole.log(this.name);//dada}};//调用obj对象的方法obj.objFun();//this 绑定到当前对象,也就是obj对象

//定义一个对象obj,添加属性name,添加方法objFunvarobj = {name:'dada',objFun:function(){console.log(this);//windowconsole.log(typeofthis);//objectconsole.log(this===window);//trueconsole.log('dada的,名字'+this.name+'大帅哥'); }};vartest = obj.objFun;test();

可以看出函数内部中this值不是静态的,是动态的,可以改变的,每次调用一个函数时,它总是在重新求值。函数内部中的this值,实际上是由函数被调用的父作用域提供,依赖实际函数的语法。

// 第一种情况//调用obj对象的方法obj.objFun();//this 绑定到当前对象,也就是obj对象// 第二种情况vartest = obj.objFun;test();

vartest = obj.objFun// 这里没有调用函数test()// 这里调用了函数// test不是一个对象的引用,所以this值代表全局对象。

当通过new运算符来调用函数时,函数被当做一个构造函数,this指向构造函数创建出来的对象。

new创建出来了一个构造函数,这个时候this的值,指向构造函数创建出来的对象。

varname ='dada';functionA(){console.log(this.name);} A();// dadavarB =newA();//undefined (因为B并没有name属性)VM162:3dadaVM162:3undefinedundefined

varname ='windowdada';varobj = {name:'objdada',objB: {name:'objBdada',bFun:function(){console.log(this.name); } }};vartest = obj.objB.bFun();vartest2 = obj.objB.bFun;test2();vartest3 = obj.objB;vartest4 = test3.bFun;test4();

注意()的近邻的左边,如果这个的左边是一个引用,那么传递给调用函数的this值为引用所属的这个对象,否则this指向为全局对象。

vartest = obj.objB.bFun();// ()左边是bFun引用,它指向objB这个对象,所有打印出objBdada

vartest2 = obj.objB.bFun;test2();// ()的左边为test2,它不是某个对象的引用,所以是全局对象// 打印出 objBdada

vartest4 = test3.bFun;test4();// 同理这个也是

JavaScript中this的原理

varname ='windowDada';varobj = {name:'dada',foo:function(){console.log(this.name); }};varfoo = obj.foo;// 写法一obj.foo()// 写法二foo()VM593:5dadaVM593:5windowDada

这个时候我相信你已经看懂了。this指向的是函数运行时所在的环境,对于obj.foo()来说,foo运行在obj环境中,所以这个时候的this指向为obj这个对象,对于foo()来说,foo运行是全局环境,这个this的指向为全局环境。(你会问为什么呢?一个指向obj这个对象,一个运行环境为全局环境,这里可以运用()左边方法)

对呀为什么呢?函数的运行环境是怎么决定在哪种情况的?

为什么obj.foo()的环境就在obj这个环境中,而作为

var foo = obj.foo,foo()的运行环境就变成了全局的执行环境呢?

this的指向设计,跟内存的数据结构有关。

varobj = {name:'dada'};

当一个对象赋值给一个变量obj的时候,JavaScript引擎会在内存里,先生成一个对象为 { name: 'dada' },然后才把这个对象的内存地址赋值给这个变量 obj。

我们说过了很多很多遍了,都知道这个变量obj就是一个地址,这个时候如果要读 obj.foo,那么引擎就会从这个变量 obj中拿内存地址,然后再从这个地址 读取原始对象,返回它的foo属性。

注意:原始的对象(开始创建的对象 { name: 'dada' })以字典结构保存的,每个属性名都对应一个属性描述对象。foo属性的值保存在属性描述对象的value属性里面。

this指包含它的函数作为方法被调用时所属的对象。

this,第一包,含它的函数,第二,作为方法被调用时,第三,所属的对象。

functionda(){console.log(this);//window}da();

会调用我,我就是谁的,谁最后调用我,我就是谁的。

testFunda()函数是在全局中被window对象所调用的,this的指向为window对象,而nameda变量在testFunda函数中,window对象中没有这个变量,所以打印不出来。

注意()的左边为testFunda

而testFunda()函数是在全局中被window对象所调用的哦!

因此this的指向就是window对象哦!

varnamedada ='dada'functiontestFundada(){varnamedada="hello dada!";console.log(this.namedada);}testFundada();VM717:4dada

看这个代码当然打印出的是dada啦,因为从全局调用,全局中有这个属性,就打印这个属性。

this被包含中一个函数中,但是这个函数被另个函数包含的情况下,这个this的指向为顶部的函数。

varobj={a:"da",b:function(){vara="dada";console.log(this.a); }};obj.b();VM726:5da

this被包含在函数b()中,因为是被obj对象所调用的,所以这个this属于这个obj对象,打印出来的就是da这个字符串了。

谁最后调用我,我就属于谁!

varobj = {a:1,b:{fn:function(){console.log(this.a);//undefined} }};obj.b.fn();VM730:5undefined

对象obj是在window上定义的,所以如下显示:

obj.b.fn()=window.obj.b.fn()

谁先调用我不算,谁最后调用我才算,window,那么this不是指向全局的对象了吗,但是最后的是被fn()调用,()左边为b对象,所以this就指向这个b对象了,因为函数中没有这个变量,所以为undefined。

出一道考题

结果是啥?我知道为2,你知道吗?那看看执行结果吧!

varobj = {name:1,b:{name:2,fn:function(){varname =3console.log(this.name); } }};obj.b.fn();

函数情况,属性的值为一个函数

varobj = {foo:function(){} };

在JavaScript引擎中会将函数单独保存在内存中,再将函数的地址赋值给foo属性的value属性。

{foo: { [[value]]: 函数的地址 ... }}

varf =function(){};varobj = {f: f };// 单独执行f()// obj 环境执行obj.f()

varfda =function(){console.log('da');};varobjDada = {f: fda };// 单独执行fda()// objDada 环境执行objDada.fda()VM858:2da

环境的考虑,在JavaScript中运行在函数体内部,引用当前环境的其他变量。在JavaScript中,由于函数可以在不同的运行环境执行,就要一种机制,使能够在函数体内部获取当前的运行环境。

this的出现,目的在于就是指代函数当前的运行环境。

this 指代全局对象

functiontest(){this.x =1; alert(this.x);}test();// 1

this 指代上级对象

functiontest(){ alert(this.x);}varo = {};o.x =1;o.m = test;o.m();// 1

this 指代 new 出的对象

varx =3;functiontest(){this.x =1;}varo =newtest();alert(x);// 3alert(o.x);// 1

函数的不同使用场合,this 有不同的值,this是函数运行时所在的环境对象。

call的用法

call(thisObj,arg1,arg2,arg...)

调用一个对象的方法,以另一个对象替换当前对象,call方法用来代替另一个对象调用一个方法,该方法可以将一个函数对象的上下文改变为由this obj指定的新对象。

call方法的参数,如果是不传,或是null,undefined的情况下,函数中的this指向就是指window对象,如果传递的是另一个函数的函数名,函数中的this指向就是这个函数的引用,如果参数传递的是基本类型数据时,函数中的this指向就是其对应的 包装对象了,如果参数传递的是一个对象时,函数中的this就指向这个对象。

一个函数的函数名,函数名是引用,所以指向是指这个函数的引用

一个对象,所以this就指向这个对象

基本类型数据,就指向这个包装对象

Function.prototype.call(thisArg[,arg1[,arg2, ...]])

当以thisArg和可选择的arg1,arg2等等作为参数在一个func对象上调用call方法。

varda = {name:"da",sayHello:function(age){console.log("hello, i am ",this.name +" "+ age +" years old"); }};varjeskson = {name:"jeskson",};da.sayHello(12);VM891:4hello, i am da12years oldundefinedda.sayHello.call(jeskson,13);VM891:4hello, i am jeskson13years oldundefined

在JavaScript中,call和apply作用是一样的

为了改变某个函数运行时的上下文(context)而存在的,就是为了改变函数体内部this的指向。

每个函数都包含两个非继承而来的方法:

call()和apply()

apply的用法

apply(thisObj,argArray)

apply方法应用于某一个对象的一个方法

用另一个对象替换当前对象。

区别:参数书写方式不同

call() 方法分别接受参数。

apply() 方法接受数组形式的参数。

Math.max(1,2,3);// 会返回 3Math.max.apply(null, [1,2,3]);// 也会返回 3Math.max.apply(Math, [1,2,3]);// 也会返回 3Math.max.apply(" ", [1,2,3]);// 也会返回 3Math.max.apply(0, [1,2,3]);// 也会返回 3

call(thisObj, arg1, arg2, arg3, arg4);apply(thisObj, [args]);thisObj:call和apply第一个参数是一样的该参数将替代Function类里面的this对象arg1,arg2....是一个个的参数args一个数组或类数组,是一个参数列表

JavaScript 严格模式

如果 apply() 方法的第一个参数不是对象,它将成为被调用函数的所有者(对象)。

在“非严格”模式下,它成为全局对象。

参考:

http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ES6基础-ES6 class

    面向对象,即万物皆对象,面向对象是我们做开发一种的方式,开发思维,面向对象的思维中万物皆对象,以人作为例子,它的特性有哪些。比如有姓名,性别,出生年月,身高等,...

    达达前端
  • 你知道多少this,new,bind,call,apply?那我告诉你

    那么什么是this,new,bind,call,apply呢?这些你都用过吗?掌握这些内容都是基础中的基础了。如果你不了解,那还不赶快去复习复习,上网查阅资料啥...

    达达前端
  • 夜谈Java类的定义

    对于结构化程序设计:起床,洗脸,吃饭,下课,放学 对于面向对象设计,类,类中的方法

    达达前端
  • JavaScript 中 this 的使用技巧总结

    在 js 中,this 这个上下文总是变化莫测,很多时候出现 bug 总是一头雾水,其实,只要分清楚不同的情况下如何执行就 ok 了。

    哲洛不闹
  • vue实现分页组件

    分页需要的字段:当前页(curPage),每页大小(pageSize),总页数(total) 作为一个组件,所以以上这些参数最好是从父组件传递过来,可以如下定义...

    陨石坠灭
  • 通过vue.js 学习来总结es6语法中的箭头函数,箭头函数原理分析。

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/acoolgiser/article/details/...

    acoolgiser
  • JavaScript 的 this 小结纯粹的函数调用作为对象方法的调用作为构造函数调用apply 调用

    函数的不同使用场合,this有不同的值。 总的来说,this就是函数运行时所在的环境对象。 下面分情况,详细讨论

    JavaEdge
  • vue实现分页组件

    分页需要的字段:当前页(curPage),每页大小(pageSize),总页数(total) 作为一个组件,所以以上这些参数最好是从父组件传递过来,可以如下定义...

    陨石坠灭
  • 深入浅出JavaScript之this

    JavaScript中的this比较灵活,根据在不同环境下,或者同一个函数在不同方式调用下,this都有可能是不同的。但是有一个总的原则,那就是this指的是,...

    哲洛不闹
  • Javascript中关键参数this浅析

    Sb_Coco

扫码关注云+社区

领取腾讯云代金券