欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

this的四种绑定规则实例讲解

程序员文章站 2022-06-17 10:26:26
this的四种绑定规则 作为普通函数调用默认绑定 事件处理程序中使用this 严格模式下 作为对象的方法调用隐式绑定 对象属性引用链中只有最顶层影响调用位置 隐式丢失 Functi...

this的四种绑定规则 作为普通函数调用默认绑定 事件处理程序中使用this 严格模式下 作为对象的方法调用隐式绑定 对象属性引用链中只有最顶层影响调用位置 隐式丢失 Functionprototypecall 或 Functionprototypeapply 调用显式绑定 定时器内含有this 硬绑定 API调用的上下文 构造器调用new绑定

this的四种绑定规则

JavaScript 的 this 总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境

1.作为普通函数调用–默认绑定

当函数作为普通函数调用,也就是不作为某个对象的属性调用时,函数中的this指向全局对象window。

window.name = 'globalName'; 
var getName = function(){ 
    return this.name; 
}; 
console.log( getName() );    // globalName 
window.name = 'globalName'; 
var myObject = { 
    name: 'sven', 
    getName: function(){ 
        return this.name; 
    } 
}; 
var getName = myObject.getName;
 //将getName()方法传递给变量getName,此时getName()方法还未执行。
console.log( getName() );    // globalName 当执行的时候,getName()是作为普通函数调用的。函数的执行环节是window对象。
console.log(myObject.getName()); //sven。 执行环境是myObject对象。

事件处理程序中使用this

var btn = document.getElementById("btn");
btn.onclick = function eve(){
    alert(this.id);     //btn
    function callback(){
        alert(this);  
    }
    callback();        //window
}

以上代码,事件处理程序中eve()中的this,指向它的调用者,也就是btn。但是回调函数callback()是在事件处理程序内部执行的,它并没有被任何对象调用,其this指向window。

如果想让callback的this指向btn,可以用一个变量保存对btn的引用:

btn.onclick = function eve(){
    var that = this;
    function callback(){
        alert(that.id);  
    }
    callback();        //btn
}

严格模式下

如果使用严格模式(strict mode),那么全局对象将无法使用默认绑定,因此 this 会绑定到 undefined:

function foo() {  
    "use strict"; 
    console.log( this.a );  
}  
var a = 2; 
foo();  // Uncaught TypeError: Cannot read property 'a' of undefined

2.作为对象的方法调用–隐式绑定

当函数作为对象的方法被调用时,this 指向该对象:

var obj = { 
    a: 1, 
    getA: function(){ 
        alert ( this === obj );    // true 
        alert ( this.a );    //  1 
    } 
}; 
obj.getA(); 
function foo() {  
    console.log( this.a ); 
} 
var obj = {  
    a: 2, 
    foo: foo  
}; 
obj.foo(); // 2  调用foo()时,this指向obj

以上代码,当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。调用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的。

对象属性引用链中只有最顶层影响调用位置。

function foo() {  
    console.log( this.a ); 
} 
var obj2 = {  
    a: 42, 
    foo: foo  
}; 
var obj1 = {  
    a: 2, 
    obj2: obj2  
}; 
obj1.obj2.foo(); // 42   this指向obj2

隐式丢失

一个最常见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象或者 undefined 上(严格模式)。

var a = "oops, global"; // a 是全局对象的属性 
function foo() {  
    console.log( this.a ); 
} 
var obj = {  
    a: 2, 
    foo: foo  
}; 
var bar = obj.foo; // 函数别名!  
bar();     // "oops, global"
var bar = obj.foo;
相当于:
var bar = function{  
    console.log( this.a ); 
} 
//将obj的foo属性传递给变量bar,此时foo()函数并没有执行,执行将函数体给到变量bar。

以上代码,虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身,因此此时的bar() 其实是一个不带任何修饰的函数调用,应用了默认绑定。

3.Function.prototype.call 或 Function.prototype.apply 调用–显式绑定

隐式绑定时,必须在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把 this 间接(隐式)绑定到这个对象上。
  如果不想在对象内部包含函数引用,而想在某个对象上强制调用函数,可以使用call()和apply()方法。它们会把这个对象绑定到this,接着在调用函数时指定这个 this。这种方式称为显示绑定。

var obj1 = { 
    name: 'sven', 
    getName: function(){ 
        return this.name; 
    } 
}; 
var obj2 = { 
    name: 'anne' 
}; 

console.log( obj1.getName() );     //  sven 
console.log( obj1.getName.call( obj2 ) );    //anne ,call()方法改变this指向,使getName中的this指向obj2.
function foo() {  
    console.log( this.a ); 
} 
var obj = {a:2}; 
foo.call( obj ); // 2
// 通过 foo.call(),在调用 foo 时强制把它的 this 绑定到 obj 上。

定时器内含有this

function foo() {
    setTimeout(function(){
        console.log( this.a ); 
    },3000) 
} 
var a = 3;
var obj = { a:2}; 
foo.call( obj ); // 3

以上代码中call()无法改变this的指向。3s后才调用定时器,但是定时器是window对象的方法,所以this还是指向定时器的调用者window。

解决方式1:

function foo() {
    var that = obj;
    setTimeout(function(){
        console.log( that.a ); 
    },3000) 
} 
var a = 3;
var obj = {a:2}; 
foo(); // 2

解决方式2:

function foo() {
    setTimeout(function(){
        console.log( this.a ); 
    }.bind(obj),3000) 
} 
var a = 3;
var obj = {a:2}; 
foo(); // 2

解决方式3:

function foo() {
    setTimeout(() =>{
        console.log( this.a ); 
    },3000) 
} 
var a = 3;
var obj = {a:2}; 
foo.call(obj); // 2

硬绑定

function foo() {  
    console.log( this.a ); 
} 
var obj = {  a:2 }; 
var bar = function() { 
    foo.call( obj ); 
}; 
bar(); // 2 
var bar = function() { 
    foo.call( obj ); 
}; 
相当于:
var bar = function{  
console.log( obj.a ); 
} 
//使用call()方法时,foo内部的this就已经执行了obj。

ES5中提供了内置的bind()方法来实现硬绑定,

bind(..) 会返回一个硬编码的新函数,它会把参数设置为 this 的上下文。

function foo() {  
    console.log( this.a ); 
} 
var obj = {  a:2 }; 
var bar = foo.bind( obj ); 

bar(); // 2

API调用的上下文

JavaScript 语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被称为“上下文”(context),其作用和 bind(..) 一样,确保回调函数使用指定的 this。

function foo(el) {  
    console.log( el, this.id ); 
} 
var obj = { id: "awesome" }; 
[1, 2, 3].forEach( foo, obj );   // 调用 foo(..) 时把 this 绑定到 obj 
// 1 awesome 2 awesome 3 awesome

这些函数实际上就是通过 call(..) 或者 apply(..) 实现了显式绑定。

4.构造器调用–new绑定

function foo(a) {  
    this.a = a; 
}  
var bar = new foo(2); 
console.log( bar.a ); // 2

以上代码,使用 new 来调用 foo(..) 时,会构造一个新对象并把它绑定到 foo(..) 调用中的 this上。

var MyClass = function(){ 
    this.name = 'sven'; 
}; 
var obj = new MyClass(); 
alert ( obj.name );     // sven 

如果构造函数返回了一个引用类型的值,最终的运算结果就会返回这个值:

var MyClass = function(){ 
    this.name = 'sven'; 
    return {    // 显式地返回一个对象 
        name: 'anne' 
    } 
}; 
var obj = new MyClass(); 
alert ( obj.name );     //:anne