掌握 JavaScript 中的 this, call, apply 的原理

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

function A(){

  //在A函数中定义一个B函数

  function B(){

    console.log(this); //Window

    console.log(typeof this); //object

    console.log(this === window); //true

  }

  //在A函数内部调用B函数

  B();

}

//调用A函数

A();

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

//定义一个对象obj,添加属性name,添加方法objFun

var obj = {

  name: 'dada',

  objFun: function(){

    console.log(this); // Object {name: "dada"}

    console.log(typeof this); //object

    console.log(this === window); //false

    console.log(this.name); //dada

  }

};

 

//调用obj对象的方法

obj.objFun(); //this 绑定到当前对象,也就是obj对象

//定义一个对象obj,添加属性name,添加方法objFun

var obj = {

  name: 'dada',

  objFun: function(){

    console.log(this); //window

    console.log(typeof this); //object

    console.log(this === window); //true

    console.log('dada的,名字'+this.name+'大帅哥'); 

  }

};

 

var test = obj.objFun;

test();

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

// 第一种情况


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

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

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


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

var name = 'dada';

function A(){

  console.log(this.name);

}

 

A(); // dada

 

var B = new A(); //undefined (因为B并没有name属性)

VM162:3 dada

VM162:3 undefined

undefined

var name = 'windowdada';

var obj = {

  name: 'objdada',

  objB: {

    name: 'objBdada',

    bFun: function(){

      console.log(this.name);

    }

  }

};


var test = obj.objB.bFun();
var test2 = obj.objB.bFun; test2();
var test3 = obj.objB;
var test4 = test3.bFun; test4();


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

var test = obj.objB.bFun();

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

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

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

JavaScript中this的原理

var name = 'windowDada';

var obj = {

  name: 'dada',

  foo: function () {

   console.log(this.name);

  }

};


var foo = obj.foo;
// 写法一 obj.foo()
// 写法二 foo() VM593:5 dada VM593:5 windowDada

这个时候我相信你已经看懂了。this指向的是函数运行时所在的环境,对于obj.foo()来说,foo运行在obj环境中,所以这个时候的this指向为obj这个对象,对于foo()来说,foo运行是全局环境,这个this的指向为全局环境。(你会问为什么呢?一个指向obj这个对象,一个运行环境为全局环境,这里可以运用()左边方法)
对呀为什么呢?函数的运行环境是怎么决定在哪种情况的?
为什么obj.foo()的环境就在obj这个环境中,而作为
var foo = obj.foo,foo()的运行环境就变成了全局的执行环境呢?
this的指向设计,跟内存的数据结构有关。

var obj = {

 name: 'dada'

};

当一个对象赋值给一个变量obj的时候,JavaScript引擎会在内存里,先生成一个对象为 { name: ‘dada’ },然后才把这个对象的内存地址赋值给这个变量 obj。
我们说过了很多很多遍了,都知道这个变量obj就是一个地址,这个时候如果要读 obj.foo,那么引擎就会从这个变量 obj中拿内存地址,然后再从这个地址 读取原始对象,返回它的foo属性。

注意:原始的对象(开始创建的对象 { name: ‘dada’ }
)以字典结构保存的,每个属性名都对应一个属性描述对象。foo属性的值保存在属性描述对象的value属性里面。
this指包含它的函数作为方法被调用时所属的对象。
this,第一包,含它的函数,第二,作为方法被调用时,第三,所属的对象。

function da(){

     console.log(this); //window

 }

da();

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

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

testFun
da
()函数是在全局中被window对象所调用的哦!
因此this的指向就是window对象哦!

var namedada = 'dada'

function testFundada () {

    var namedada="hello dada!";

    console.log(this.namedada);

}

testFundada();

VM717:4 dada

看这个代码当然打印出的是dada啦,因为从全局调用,全局中有这个属性,就打印这个属性。
this被包含中一个函数中,但是这个函数被另个函数包含的情况下,这个this的指向为顶部的函数。

var obj={

    a:"da",

    b:function(){

        var a="dada";

        console.log(this.a);

    }

};

obj.b();

VM726:5 da

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

var obj = {

    a: 1,

    b:{

        fn:function(){

            console.log(this.a); //undefined

        }

    }

};

obj.b.fn();


VM730:5 undefined

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

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

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


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

var obj = {

    name: 1,

    b:{ 

        name: 2,

        fn:function(){

           var name = 3

            console.log(this.name);

        }

    }

};

obj.b.fn();


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

var obj = { foo: function () {} };

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

{

  foo: {

    [[value]]: 函数的地址

    ...

  }

}

var f = function () {};

var obj = { f: f };


// 单独执行 f()
// obj 环境执行 obj.f()

var fda = function () {

 console.log('da');

};


var objDada = { f: fda };
// 单独执行 fda()
// objDada 环境执行 objDada.fda() VM858:2 da

环境的考虑,在JavaScript中运行在函数体内部,引用当前环境的其他变量。在JavaScript中,由于函数可以在不同的运行环境执行,就要一种机制,使能够在函数体内部获取当前的运行环境。
this的出现,目的在于就是指代函数当前的运行环境。
this 指代全局对象

function test(){

    this.x = 1;

    alert(this.x);

}

test();    // 1

this 指代上级对象

function test(){

    alert(this.x);

}

var o = {};

o.x = 1;

o.m = test;

o.m();    // 1

this 指代 new 出的对象

var x = 3;


function test(){ this.x = 1; }
var o = new test();
alert(x); // 3
alert(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方法。

var da = {

    name: "da",

    sayHello: function (age) {

         console.log("hello, i am ", this.name + " " + age + " years old");

     }

};


var jeskson = { name: "jeskson", };
da.sayHello(12); VM891:4 hello, i am da 12 years old undefined da.sayHello.call(jeskson,13); VM891:4 hello, i am jeskson 13 years old undefined


在JavaScript中,call和apply作用是一样的
为了改变某个函数运行时的上下文(context)而存在的,就是为了改变函数体内部this的指向。
每个函数都包含两个非继承而来的方法:
call()和apply()

apply的用法

apply(thisObj,argArray)

apply方法应用于某一个对象的一个方法
用另一个对象替换当前对象。

区别:参数书写方式不同

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

Math.max(1,2,3);  // 会返回 3


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

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


JavaScript 严格模式
如果 apply() 方法的第一个参数不是对象,它将成为被调用函数的所有者(对象)。
在“非严格”模式下,它成为全局对象。
参考:
http://www.ruanyifeng.com/blog/2018/06/javascript-this.html