javascript中this 的指向问题(二)

接上一篇,(一)

三:显式绑定
如果想要在某个对象上强制调用函数,通过call,apply方法,第一个参数是一个对象,是给this准备的,接着在调用函数时将其绑定到this。因为可以直接指定this的绑定对象,所以称为显式绑定。
上代码:

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

通过foo.call(obj);在调用foo时强制把他的this绑定到obj上。
1、如果你传入了一个原始值(字符串类型、布尔类型或者数字类型)来当作this的绑定对象,这个原始值会被转换成它的对象形式(也就是new String(。。)等),这被称为“装箱”。
显示绑定仍然无法解决丢失绑定的问题。因此就出现了下面的几种情况:
一一:硬绑定:显式的强制绑定
上代码:

function foo() {
        console.log(this.a);
    }
    var obj1={
        a:1
    };
    var obj2={
        a:2
    };
    var bar=function () {
        foo.call(obj1);
    };
    bar();//1。。。foo绑定到obj1
    setTimeout(bar,100);//1。。。这个会是最后一个输出,因为使用了setTimeout
    bar.call(obj2);//1。。。想要通过bar再改变this的指向已经不能改变了
    foo.call(obj2);//2。。。直接通过foo绑定obj2还是可以绑定的,因为foo函数又不是obj1一个人的
    foo.bind(obj1).call(obj2);//1。。。bind也是绑定对象的一种方法,绑定之后也无法改变,而且这个会输出1下面的一行代码还会输出一个错误的原因是bind绑定完obj1执行完foo后还会返回一个函数,用于后面的call方法绑定,但是this的指向已经不能改变了,但是还可以通过call方法继续传参数。
    foo.call(obj1).call(obj2);//先输出一个1,再输出TypeError: Cannot read property 'call' of undefined。。。。。。而这个foo第一个call方法执行完并不会返回一个函数,所以执行到后面的那个call的时候系统就会报错
代码解读:创建了函数bar,并在他的内部手动调用了foo.call(obj1);因此强制把foo的this绑定到了obj1上。无论之后如何调用函数bar,它都会手动在obj1上调用foo。这种绑定就是一种显式的强制绑定,称为硬绑定。

硬绑定的应用场景:创建一个包裹函数,负责接收参数并返回值:
上代码:

    function foo(something) {
        console.log(this.a);//2
        return this.a+something;
    }
    var obj={
        a:2
    };
    var bar=function () {
        return foo.apply(obj,arguments);
    };
    var b=bar(3);
    console.log(b);//5

另一种使用方法是创建一个可以重复使用的辅助函数:
上代码:

function foo(something) {
        console.log(this.a,something);//2,3
        return this.a+something;
    }
//    简单的辅助绑定函数
    function bind(fn, obj) {
        return function () {
            return fn.apply(obj,arguments);
        }
    }
    var obj={
        a:2
    };
    var bar=bind(foo,obj);
    var b=bar(3);
    console.log(b);//5

由于硬绑定是一种非常常用的模式,ES5提供了内置的方法Function.prototype.bind

function foo(something) {
        console.log(this.a,something);//2,3
        return this.a+something;
    }
    var obj={
        a:2
    };
    var bar=foo.bind(obj);
    var b=bar(3);
    console.log(b);//5

bind(…)会返回一个硬编码的新函数,它会把你指定的参数设置为this的上下文并调用原始函数。硬绑定第一个例子中用到的bind就是这个。

二二:API调用的上下文

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

forEach函数:
array.forEach(callback [,thisobj]);下面链接给了详细的forEach用法:
https://msdn.microsoft.com/library/ff679980(v=vs.94).aspx
这些函数实际上都是通过call或者apply实现了显示绑定,这样写可以少写一写代码而已。

四:new绑定

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

使用new来调用foo时,会构造一个新对象并把它绑定到foo调用中的this上,new是最后一种可以影响函数调用时this绑定行为的方法,称为new绑定。
从理论上来讲:
首先来看,使用new来调用函数的时候发生了什么!
1、创建一个全新的对象;
2、这个新对象会被执行prototype连接;
3、这个新对象会绑定到函数调用的 this;
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

以上。。。四种this的指向问题已经介绍完了, 但是实际现实中总是会碰到更复杂的有关this的问题。
如果某个调用位置可以应用多条规则,,就要考虑这四种规则的优先级问题了!下面一个话题,优先级!

优先级

其实就是通过代码的运行结果来判断,各种规则的优先级。
毫无疑问,默认绑定的优先级是四种规则中最低的。

1、显示绑定>隐式绑定:

function foo(a) {
        console.log(this.a);
    }
    var obj1={
        a:2,
        foo:foo
    };
    var obj2={
        a:3,
        foo:foo
    };
//    隐式绑定
    obj1.foo();//2
    obj2.foo();//3
//    显式绑定
    obj1.foo.call(obj2);//3
    obj2.foo.call(obj1);//2

2、new绑定>隐式绑定

function foo(something) {
        this.a=something;
    }
    var obj1={
        foo:foo
    };
    var obj2={};

//    隐式绑定
    obj1.foo(2);
    console.log(obj1.a);//2

//    显式绑定
    obj1.foo.call(obj2,3);
    console.log(obj2.a);//3

//    new绑定
    var bar =new obj1.foo(4);
    console.log(obj1.a);//2
    console.log(bar.a);//4

3、new绑定>显式绑定

function foo(something) {
        this.a=something;
    }
    var obj1={};

//    显式绑定
    var bar=foo.bind(obj1);
    bar(2);
    console.log(obj1.a);//2

//    new绑定
    var baz =new bar(3);
    console.log(obj1.a);//2
    console.log(baz.a);//3

bar被硬绑定到obj1上,但是new bar(3)并没有改变obj1.a=3的值。但是new还是修改了bar中的this。具体为啥。。有关bind的实现。更复杂,我还没有搞懂,23333zzz

在new中使用硬绑定函数的主要目的:预先设置函数的一些参数,这样在使用new进行初始化时就可以只传入其余的参数。(有关“部分应用”,“柯里化”)
例如:`function foo(p1,p2) {
    this.val=p1+p2;
}
//  之所以只用null是因为在本例中我们并不关心硬绑定的this是什么
//    反正使用new时this会被修改
var bar=foo.bind(null,"p1");
var baz =new bar("p2");
console.log(baz.val);//p1p2`

所以,,,综上所述:new绑定>显式绑定>隐式绑定>默认绑定
判断this的方法:
1、函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象

var bar=new foo();

2、函数是否通过call、apply(显示绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。

var bar =foo.call(obj2);

3、函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。

var bar=obj1.foo();

4、如果都不是的话,使用默认绑定。如果在严格模式下就绑定到undefined,否则绑定到全局对象。

var bar=foo();

理解了一、二this绑定就基本明白了,不过。。凡事总有例外。下次再接着总结绑定例外和ES6中的this的用法,,,啊,这个this确实蛮复杂的,我也看了好久,,,还是这种系统的讲容易理解,再加上接触的多了慢慢就明白了。。。orz
应该还会有一个javascript中this 的指向问题(三)= =….

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值