欢迎光临
我们一直在努力

JavaScript | this到的四种绑定规则

文章目录

默认绑定

全局环境中,this默认绑定到window

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

函数独立调用时,this默认绑定到window

function fn(){
    console.log(this === window);
}
fn();//true

被嵌套的函数独立调用时,this默认绑定到window

如下所示,在对象中创建嵌套函数,当被嵌套的函数被独立调用时,this指针指向window,而非对象

var a = 0;
var obj={
    a:1,
    fn:function(){
        function test(){
            console.log(this.a);
        }
        test();
    }
}
obj.fn(); //0

IIFE

IIFE立即执行函数实际上是函数声明后直接调用执行,因此被函数嵌套的IIFE下的this指针仍然指向window

var a = 0;
var obj = {
    a : 2,
    fn:fn
}
function fn(){
    (function(){
        console.log(this.a)
    })();
}
obj.fn(); //0
var a = 0;
var obj = {
    a : 2,
    fn:function(){
        (function(){
            console.log(this.a)
        })();
    }
}
obj.fn(); //0

闭包

正如前面所说,被嵌套的函数是独立调用时,this默认绑定到window,因此闭包内的函数调用this同样是绑定到window

var a = 0;
function fn(){
    function fn2(){
        console.log(this.a);
    }
    return fn2;
}
var obj = {
    a : 1,
    fn : fn
}
obj.fn()(); //0

在使用闭包时,如果想访问嵌套函数的this,常常会在嵌套函数中使用var that = this,然后在闭包中替代this,使用作用域查找的方法来找到嵌套函数的this值

var a = 0;
function fn(){
    var that = this;
    function fn2(){
        console.log(this.a);
        console.log(that.a);
    }
    return fn2;
}
var obj = {
    a : 1,
    fn : fn
}
obj.fn()(); //0 1

隐式绑定

被直接对象所包含的函数调用时,也就是方法调用,this隐式绑定到该直接对象

function fn(){
    console.log(this.a)
};
var obj1 = {
    a : 2,
    fn :fn,
    obj2 : {
        a : 3,
        fn : fn
    }
}
obj1.fn();       //2
obj1.obj2.fn();  //3

隐式丢失

隐式丢失指的是被隐式绑定的函数丢失绑定对象,从而this指针将指向window。

函数别名

var a = 0;
function foo(){
    console.log(this.a);
};
var obj = {
    a : 2,
    foo:foo
}
//把obj.foo赋予别名bar,造成了隐式丢失,因为只是把foo()函数赋给了bar,而bar与obj对象则毫无关系
var bar = obj.foo;
bar();//0

参数传递

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 foo(){
    console.log(this.a);
};
var obj = {
    a : 2,
    foo:foo
}
setTimeout(obj.foo,100);//0

间接引用

函数的“间接引用”一般都是在无意间创建的,最容易在赋值时发生,会造成隐式丢失

function foo() {
    console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
//将o.foo函数赋值给p.foo函数,然后立即执行。相当于仅仅是foo()函数的立即执行
(p.foo = o.foo)(); // 2
function foo() {
    console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
//将o.foo函数赋值给p.foo函数,之后p.foo函数再执行,是属于p对象的foo函数的执行
p.foo = o.foo;
p.foo();//4

其他情况

在JavaScript引擎内部,obj和obj.foo储存在两个内存地址,简称M1和M2。只有obj.foo()这样调用时,是从M1调用M2,此时this指针指向obj。但是,下面几种情况都是直接取出M2进行运算,然后在全局环境执行运算结果(还是M2),因此this指向全局环境

var a = 0;
var obj = {
    a : 2,
    foo:foo
};
function foo() {
    console.log( this.a );
};
(obj.foo = obj.foo)();//0
(false || obj.foo)();//0
(1, obj.foo)();//0

显示绑定

通过call()、apply()、bind()方法把对象绑定到this上,叫做显式绑定。对于被调用的函数来说,叫做间接调用

var a = 0;
function foo(){
    console.log(this.a);
}
var obj1 = {
    a:1
};
var obj2 = {
    a:2
};
foo.call(obj1);//1
foo.call(obj2);//2

注意:隐式绑定无法解决隐式丢失的问题

硬绑定

把显示绑定放在一个函数中,是this不能再被修改,这种方法叫做硬绑定

var a = 0;
function foo(){
    console.log(this.a);
}
var obj = {
    a:2
};
var bar= function(){
    foo.call(obj);
}
//在bar函数内部手动调用foo.call(obj)。因此,无论之后如何调用函数bar,它总会手动在obj上调用foo
bar();//2
setTimeout(bar,100);//2
bar.call(window);//2

API绑定

javascript中新增了许多内置函数,具有显式绑定的功能,如数组的5个迭代方法:map()、forEach()、filter()、some()、every()

var id = 'window';
function foo(el){
    console.log(el,this.id);
}
var obj = {
    id: 'fn'
};
[1,2,3].forEach(foo);//1 "window" 2 "window" 3 "window"
[1,2,3].forEach(foo,obj);//1 "fn" 2 "fn" 3 "fn"

new绑定

如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。对于this绑定来说,称为new绑定

【1】构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值

function fn(){
    this.a = 2;
}
var test = new fn();
console.log(test);//{a:2}

【2】如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果

function fn(){
    this.a = 2;
    return;
}
var test = new fn();
console.log(test);//{a:2}

【3】如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象

var obj = {a:1};
function fn(){
    this.a = 2;
    return obj;
}
var test = new fn();
console.log(test);//{a:1}

注意:尽管有时候构造函数看起来像一个方法调用,它依然会使用这个新对象作为this。也就是说,在表达式new o.m()中,this并不是o

var o = {
    m: function(){
        return this;
    }
}
var obj = new o.m();
console.log(obj,obj === o);//{} false
console.log(obj.constructor === o.m);//true

严格模式

【1】严格模式下,独立调用的函数的this指向undefined

function fn(){
    'use strict';
    console.log(this);//undefined
}
fn();

function fn(){
    console.log(this);//window
}
fn();

【2】在非严格模式下,使用函数的call()或apply()方法时,null或undefined值会被转换为全局对象。而在严格模式下,函数的this值始终是指定的值

var color = 'red';
function displayColor(){
    console.log(this.color);
}
displayColor.call(null);//red

var color = 'red';
function displayColor(){
    'use strict';
    console.log(this.color);
}
displayColor.call(null);//TypeError: Cannot read property 'color' of null

总结

this的四种绑定规则:默认绑定、隐式绑定、显式绑定和new绑定,分别对应函数的四种调用方式:独立调用、方法调用、间接调用和构造函数调用。

分清这四种绑定规则不算难,比较麻烦的是需要练就火眼金睛,识别出隐式丢失的情况

赞(2) 打赏
未经允许不得转载:散人研 » JavaScript | this到的四种绑定规则
分享到: 更多 (0)

评论 抢沙发

1 + 9 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏