JavaScript学习(7)——this

        好记性不如烂笔头,在写作的过程中自己消化吸收,所以写下这个JavaScript学习系列文章。文章中的文字都是自己理解的内容,各位酌情观看。

        上一篇讲到this的基础概念,这篇详细讲讲this的具体绑定规则。在回顾一下规则的具体内容强化一下。

1. 函数是否在 new 中调用?如果是的话 this 绑定的是新创建的对象。

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

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

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

1.new调用

         通过new调用的this指向优先级最高。函数是否在 new 中调用?如果是的话 this 绑定的是新创建的对象。JavaScript创建对象的过程:

使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

1. 创建(或者说构造)一个全新的对象。

2. 这个新对象会被执行 [[ 原型 ]] 连接。

3. 这个新对象会绑定到函数调用的 this。

4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

         也就是说使用new创建一个全新的对象bar,this就指向这个新建的对象bar。

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

2.call apply显示绑定

        当我们不想在对象内部包含函数引用,而想在某个对象上强制调用函数,我们可以使用call或apply显示绑定this。它们的第一个参数是一个对象,它们会把这个对象绑定到 this,接着在调用函数时指定这个 this。

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

        可以看到控制台会输出20 然后输出2;第一次因为没有绑定,所以指向的window(见上一章节分析),第二次通过call绑定了对象,this指向obj,this.a相当于obj.a,因此输出的2。call和apply的作用一致,只是参数形式不一样:call 的参数是直接放进去的,第一个参数是要绑定的对象,第二第三...第 n 个参数是调用函数的传参,全都用逗号分隔。apply将调动函数的传参打包成数组,只接收两个参数。

fn.call(obj,arg1,arg2,arg3,arg4);
fn.apply(obj,[arg1,arg2,arg3,arg4]);

 3.上下文隐式绑定

          不论我们是否知道,我们在不经意间就使用了这种规则。为了避免出现不可知的错误,我们有必要对这个知识点深刻理解一下。请记住:函数由谁调用,this就指向谁。我们来分析几个例子:

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

         第一个例子相对简单,但没把上面这句话仔细理清的同学来讲还是有问题的。我们说的是最直接调用函数的对象。在这个例子中,函数是foo,在内存中它是单独的一块空间存储了这个函数,然后有个普通变量名为foo的指针指向了这个函数所在的地址。在obj中有个foo的属性,他存的内容也是个指针,指向这个foo函数的地址。那么在执行obj.foo()的时候,实际调用的对象是obj找到foo属性,发现它指向foo函数,之际从obj调用foo,通过this把obj对象带给foo函数,因此foo调用的时候this指向了obj,因此输出了2。再看个例子深刻一下

function foo() {
    console.log( this.a );
}
var obj2 = {
    a: 42,
    foo: foo
};
var obj1 = {
    a: 2,
    obj2: obj2
};
obj1.obj2.foo(); // 42
//对象属性引用链中只有最顶层或者说最后一层会影响调用位置

         JavaScript的运算符优先级中“.”和“()”相同,那从左到右顺序执行。这个例子中obj1的obj2属性指向的obj2对象,obj2对象的foo属性指向foo函数,因此直接调用foo的是obj2。因此输出42。再看一个:

function foo() {
    console.log( this.a );
}
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo;
var a = "oops, global";
bar();

         这里bar()执行后会输出什么呢?先想一想,思考比看的过程更深刻,当思考的对了,这个知识点就掌握的彻底了。下面分析一下思路:首先定义了一个函数foo,依然在内存中有一片独立的区域存储的函数的信息,然后一个变量名为foo的普通变量存储了一个指针指向了foo函数所在的地址。然后定义了obj对象,他有个foo属性,这个属性指向的也是foo函数所在的地址;然后定义了一个bar,他等于obj.foo属性,bar其实就跟foo变量一样,都是普通变量,存储了指向了foo函数的地址。在执行bar()的时候,就相当于直接调用的foo函数,谁调用的呢?window!this指向的window变量。所以输出的结果就是"oops, global"。

4.默认绑定到全局对象

        具体分析过程见上一章节,这里不再赘述。

5.例外

        怎么还有例外?!有一种场景是我们在编写代码的时候就像确定下this的指向,无论怎么调用都不再改变this,而上面的这些规则套下来显然是无法满足这样的需求的。JavaScript提供这样一种例外。就两种情况:bind和ES6新增语法箭头函数()=>{}。他们绑定时候就不再改变,即便使用call或者apply也不会改变this绑定的指向。(临时来了个急活,干活去了...后面详细分析)

        终于可以继续了。

bind

        bind是ES5中提供的内置方法Function.prototype.bind。bind(...)会返回一个硬编码的新函数,他会把参数设置为this的上下文并调用原始函数。

//1.硬绑定
function foo(something) {
    console.log( this.a, something );
    return this.a + something;
}
var obj = {
    a:2
};
var bar = foo.bind( obj );
var b = bar.apply({a:33},[3]); // 2 3
console.log( b ); // 5

//2.常规
function foo(something) {
    console.log( this.a, something );
    return this.a + something;
}
var obj = {
    a:2
};
var bar = foo.bind( obj );
var b = foo.apply({a:33},[3]); // 33 3
console.log( b ); // 36

        bind就可以理解为原来函数的一个拷贝,但是不同的是这个新函数的this永远指向在绑定的时候传入的对象(这里就是obj)。所以例子bar与foo函数的功能是一样的,但是bar调用的时候this永远指向的obj,因此this.a=2,即便调用的时候使用apply重新绑定也无法改变;而foo函数使用apply重新绑定的时候this指向了传入的绑定对象{a:33},因此输出33 3。

箭头函数()=>{}

        箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this。

好,上代码

function foo() {
    // 返回一个箭头函数
    return (a) => {
    //this 继承自 foo()
    console.log( this.a );
    };
}
var obj1 = {
    a:2
};
var obj2 = {
    a:3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, 不是 3 !

        foo() 内部创建的箭头函数会捕获调用时 foo() 的 this。由于 foo() 的this 绑定到 obj1,bar(引用箭头函数)的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。这里我们可以在foo函数中定义一下this.a=33;想一想最后调用bar.call(obj2)的时候会输出什么呢?~~

        这里this基本就结束了,在写代码的时候再细细体会就能理解地更深刻了。后面继续JavaScript的上下文知识,期待~

        

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值