JS中的this原理详解

来源:互联网 发布:nginx http host 编辑:程序博客网 时间:2024/06/08 10:10

this的用法

  • this的用法
    • JS中的This
      • 独立调用
      • 隐式调用使用对象的属性调用
      • 显式绑定
      • new绑定
      • 绑定例外
    • 总结一
      • 总结代码示例

JS中的This

  • 注意:需要关注的是,调用位置上的调用形式;

独立调用

  • 默认绑定规则:this绑定给window;
  • 在严格模式下,默认绑定规则会把this绑定undefined上;

    function foo() {console.log( this.a );}var a = 2;(function(){"use strict";foo(); //2})();
  • 这里有一个微妙但是非常重要的细节,虽然 this 的绑定规则完全取决于调用位置,但是只有 foo()运行在非 strict mode 下时,默认绑定才能绑定到全局对象; 严格模式下调用foo()不会影响默认绑定规则;

    function foo() {"use strict";console.log( this.a );}var a = 2;foo(); //undefined
  • 无论函数是在哪个作用域中被调用,只要是独立调用则就会按默认绑定规则被绑定到全局对象或者undefined上

隐式调用:(使用对象的属性调用)

  • 隐式绑定的规则:this给离函数最近的对象;是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含;
//  隐式绑定的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含//  当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象function foo() {    console.log( this.a );//2}var obj = {    a: 2,    foo: foo};obj.foo(); 
  • 当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象;
  • 对象属性引用链中只有最顶层或者说最后一层会影响调用位置;
//对象属性引用链中只有最顶层或者说最后一层会影响调用位置function foo() {    console.log( this.a );}var obj2 = {    a: 42,    foo: foo};var obj1 = {    a: 2,    obj2: obj2};obj1.obj2.foo(); //42
  • 隐式丢失

    • 将函数通过隐式调用的形式赋值给一个变量;
      function foo() {    console.log( this.a );//oops, global}var a = "oops, global"; var obj = {    a: 2,    foo: foo};var bar = obj.foo; //把obj.foo赋予别名bar,造成了隐式丢失,因为只是把foo()函数赋给了bar,而bar与obj对象则毫无关系bar(); //等价于var a = "oops, global"; var bar = function foo(){    console.log( this.a );}bar();//oops, global
  • 将函数通过隐式调用的形式进行传参;

var a = 0;function foo(){    console.log(this.a);};function bar(fn){    fn();}var obj = {    a : 2,    foo:foo}//把obj.foo当作参数传递给bar函数时,有隐式的函数赋值fn=obj.foo。与上例类似,只是把foo函数赋给了fn,而fn与obj对象则毫无关系。bar(obj.foo);//0//等价于var a = 0;function bar(fn){    fn();}bar(function foo(){    console.log(this.a);});
  • 内置函数:内置函数与上例类似,也会造成隐式丢失
var a = 0;function foo(){    console.log(this.a);};var obj = {    a : 2,    foo:foo}setTimeout(obj.foo,100);//0//等价于var a = 0;setTimeout(function foo(){    console.log(this.a);},100);//0

显式绑定

  • 通过call()、apply()、bind()方法把对象绑定到this上,叫做显式绑定。
//普通对象的属性查找 function foo(a,b) {    console.log( this.a,a,b );}var obj = {    a:2};foo.call( obj,"a","b"); //2 a bfoo.apply(obj,["a","b"])//2 a b
  • 显式绑定规则:
  • 硬绑定:硬绑定是显式绑定的一个变种,使this不能再被修改。
//  我们来看看这个显式绑定变种到底是怎样工作的。我们创建了函数 bar() ,并在它的内部手动调用了 foo.call(obj) ,因此强制把 foo 的 this 绑定到了 obj 。无论之后如何调用函数 bar ,它总会手动在 obj 上调用 foo 。这种绑定是一种显式的强制绑定,因此我们称之为硬绑定。function foo() {    console.log( this.a );/}var a =1;var obj = {a:2};var obj_test = {a:"test"};var bar = function() {    console.log( this.a );    foo.call( obj );};bar(); // 1 2setTimeout( bar, 1000 ); // 1 2bar.call( obj_test ); //test  2   //硬绑定的bar不可能再修改它的this(指的是foo中的this)//  硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接收到的所有值    function foo(arg1,arg2) {        console.log( this.a,arg1,arg2);        return this.a + arg1;    }    var obj = {a:2};    var bar = function() {        return foo.apply( obj, arguments);    };    var b = bar(3,2); // 2 3 2    console.log( b ); // 5

new绑定

//3. 这个新对象会绑定到函数调用的 thisfunction foo(a) {    this.a = a;}var bar = new foo(2);console.log( bar.a ); // 2      //使用 new 来调用 foo(..) 时,我们会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上。 new 是最//后一种可以影响函数调用时 this 绑定行为的方法,我们称之为 new 绑定。   

绑定例外

  • 箭头函数:this的绑定和作用域有关。如果在当前的箭头函数作用域中找不到变量,就像上一级作用域里去找。
function foo() {     setTimeout(() => {        console.log('id:', this.id); //id: 42      }, 100);}var id = 21;foo.call({ id: 42 })
  • 被忽略的this:当被绑定的是null,则使用的是默认绑定规则;
//  如果你把 null 或者 undefined 作为 this 的绑定对象传入 call 、 apply 或者 bind ,这些值在调用时会被忽略,实际应用的是默认绑定规则;function foo() {    console.log( this.a );}var a = 2222;foo.call( null ); // 2
  • 柯里化
function foo(a,b) {     console.log( "a:" + a + ", b:" + b );}// 把数组“展开”成参数foo.apply( null, [2, 3] ); // a:2, b:3// 使用 bind(..) 进行柯里化var bar = foo.bind( null, [2] );bar( 3 ); // a:2, b:3//升级版:不会污染全局    function foo(a,b) {        this        console.log( "a:" + a + ", b:" + b );    }    // 我们的DMZ空对象,“DMZ”(demilitarized zone,非军事区)    var ø = Object.create( null );//{}    // 把数组展开成参数    foo.apply( ø, [2, 3] ); // a:2, b:3    // 使用bind(..)进行柯里化    var bar = foo.bind( ø, 2 );    bar( 3 ); // a:2, b:3



总结一

  • this,this是函数执行的上下文对象;

    • 根据函数的调用方式不同,this的值也不同;
    • 1、以函数的形式调用的,this是window;
    • 2、以方法的形式调用时,this是调用方法的对象;
    • 3、以构造函数的形式调用时,this是新创建的对象;
    • 4、以call()和apply()调用时,this是方法中第一个参数。
  • 根据函数调用的方式不同this的值也不同:

    • 1.以函数的形式调用,this是window
    • 2.以方法的形式调用,this是调用方法的对象
    • 3.以构造函数的形式调用,this是新创建的那个对象
    • 4.使用call和apply调用的函数,第一个参数就是this
    • 5.在全局作用域中,this是window
    • 6.在响应函数中,给谁绑定的事件this就是谁。
  • 在事件处理函数中:获取触发当前事件的元素;

  • 在普通函数中(直接调用):获取的是window对象;
  • 作为对象的方法中:获取的是当前对象。
  • 在全局作用域中:this指代window对象。
  • 构造函数中的this:指代创建的对象。
  • 注意:
    • 当调用方式不同时,this指代的含义不同,得到的结果也不同。
    • this本身不具备任何含义。

总结代码示例

<script>    // TODO 一、以全局&调用普通的函数的形式调用,this是window.    function fn(){        console.log(this);    }    fn();    //二、构造函数    //如果函数作为构造函数使用,那么其中的this就代表即将new出来的对象    function Objfn(){        this.a = 10;        console.log(this);//此时输出的是对象 Objfn {a: 10}    }    var objfn = new Objfn();    console.log('objfn.a='+objfn.a);//objfn.a=10    //但是如果直接调用Objfn1()函数,而不是new Objfn1(),那么情况就变成了Objfn()是普通函数    function Objfn1(){        this.a = 10;        console.log(this);//此时输出的是对象 Window {stop: function, open: function, alert: function, confirm: function, prompt: function…}    }    var objfn1 = Objfn1();    //console.log('objfn.a='+objfn1.a);//错误,Cannot read property 'a' of undefined    //三、对象方法    //如果函数作为对象的方法,方法中的this指向该对象    var obj={        a:10,        foo:function () {            console.log(this);//Object {a: 10, foo: function}            console.log(this.a);//10        }    }    obj.foo();    //注意,要是此时在对象方法中定义函数,那么情况就不同了    //此时的函数fn虽然是在 obj1.foo1内部定义的,但它仍然属于一个普通的函数,this仍然指向window.    var obj1={        a1:10,        foo1:function () {            function fn(){                console.log(this);//Window {stop: function, open: function, alert: function, confirm: function, prompt: function…}                console.log(this.a1);//undefined            }            fn();        }    }    obj1.foo1();    //另外,如果此时foo2不作为对象方法被调用,则    var obj2 = {        x: 10,        foo2: function () {            console.log(this);       //Window            console.log(this.x);     //undefined        }    };    var fn2 = obj2.foo2;    //等价于fn2 = function () {        //console.log(this);       //Window        //console.log(this.x);     //undefined    //}    fn2();//此时又是在全局里执行的普通函数。    //四、构造函数的prototype属性    //在 Foof.prototype.getX函数中,this 指向的 Foof对象。不仅仅如此,即便是在整个原型链中,this 代表的也是当前Foof对象的值。    function Foof(){        this.x = 10;    }    Foof.prototype.getX = function () {        console.log(this);        //Foof {x: 10}        console.log(this.x);      //10    }    var foof = new Foof();    foof.getX();    //五、函数用call、apply或者bind调用    var obja = {        x: 10    }    function fooa(){        console.log(this);     //Object {x: 10}        console.log(this.x);   //10    }    fooa.call(obja);    fooa.apply(obja);    fooa.bind(obja)();</script>