this学习(二)调用位置和绑定规则

目录

 

一:调用位置

1、如何查找函数的调用位置

二:绑定规则

1、默认绑定

2、隐式绑定

2.1、隐式丢失

3、显式绑定

3.1、硬绑定

4、new绑定


一:调用位置

1、如何查找函数的调用位置

看下面代码例子

// 找到函数的调用位置
function baz() {
    debugger
    // 当前调用栈是:baz
    // 因此当前的调用位置是全局作用域
    console.log("baz");
    bar();  // bar的调用位置
}

function bar() {
    debugger
    // 当前调用栈是baz -> bar
    // 因此,当前调用位置在baz中
    console.log("bar");
    foo();  // foo的调用位置
}

function foo() {
    debugger
    // 当前调用栈是baz -> bar -> foo
    // 因此,当前调用位置在bar中
    console.log("foo");
}

baz();  // baz的调用位置

这样查找是可以的,但是比较麻烦,我们用debugger的方式来查找会比较好。如下

在这个调用栈中,可以看到函数的调用位置,就是调用栈的第二个元素。

 

二:绑定规则

函数执行过程中调用位置如何决定this的绑定对象?

来看看下面的绑定规则

1、默认绑定

默认绑定是指函数没有任何引用进行调用的情况下运行,比如下面这个例子

function foo() {
    console.log(this.a)
}

var a = 12;
foo();      // 12

函数foo是直接运行的,由于其调用位置的作用域是全局作用域,所以this就指向了全局window。

但是如果是在严格模式下this无法绑定到window。

 

2、隐式绑定

如果函数的调用位置有上下文对象(或者说被某个对象拥有着)的时候,函数中的this会被绑定到这个对象中。例子:

function foo() {
    console.log(this.a)
}

var obj = {
    a: 21,
    foo: foo
}

obj.foo();  // 21

例子中,foo函数被调用的时候,是被obj对象所拥有/包含着的,所以函数的this被绑定到了obj对象上。

2.1、隐式丢失

先看一个例子

function foo() {
    console.log(this.a)
}

var obj = {
    a: 21,
    foo: foo
}

function foo2(fn) {
    fn()
}

var a = 'global';

foo2(obj.foo)    // global

这个例子中,foo2函数的参数也是一个函数,而在foo2的实际调用位置中的实参(obj.foo),它的引用还是foo函数,实际上是将foo函数放在foo2函数中运行而已,当foo2函数调用的时候,由于是在全局作用域下调用,所以它的this指向了全局window,所以最后输出了global。obj.foo的值,是foo函数。

 

3、显式绑定

call 和 apply,这两个方法是可以修改函数的this指向的,并且是显而易见的。例子:

function foo() {
    console.log(this.a)
}

var obj = {
    a: 10
}

foo();  // undefined
foo.call(obj);  // 10

这个例子中,如果foo函数不调用call方法,直接运行的话,this是默认绑定到全局作用域上的,但是使用call方法来显式绑定this的指向,foo的this指向就被绑定到了obj对象上了。

apply的使用方法和call基本一样,第一个参数都是绑定this的对象,第二个参数是它们唯一不同的地方,具体有什么不一样,可以看这篇文章。

3.1、硬绑定

例子

function foo() {
    console.log(this.a)
}

var obj = {
    a: 10,
    foo: foo
}

var bar = function() {
    foo.call(obj);
}

var test = bar;

test();     // 10
test.call(window);  // 10

可以看到,当在bar函数内部将foo的this绑定到obj对象上,之后无论如何调用bar函数,foo的this总是指向到obj对象的,这种绑定是一种显示的强制绑定。

另外一种硬绑定方法

例子:

function foo(arg) {
    console.log(this.a, arg)
    return this.a + arg;
}

// 辅助函数
function bind(fn, obj) {
    return function() {
        return fn.apply(obj, arguments)
    }
}

var obj = {
    a: 2
}

var bar = bind(foo, obj);
var b = bar(3);     // 2 3
console.log(b)      // 5

这个例子中,先看 var bar = bind(foo, obj);这一行代码执行过后,实际上bar是等于

/*

function () {
    return foo.apply(obj, arguments)
}

*/

此时的bar,是不是很像上一个硬绑定的例子,在函数里边对foo函数进行显示绑定,这里也就是说,无论现在你怎么执行bar函数,this都被绑定到了obj上面了。

所以,当bar(3)执行的时候(实际上是运行foo函数),this指向obj,所以输出了一个2,因为传入了一个参数:3,所以最后是输出2和3。

而b是bar函数(实际上是foo函数)运行之后的一个返回值,2 + 3 = 5。

这种实现方式就是ES5的内置方法Function.prototype.bind的实现原理,下面是内置函数bind的用法:

function foo(arg) {
    console.log(this.a, arg)
    return this.a + arg;
}

var obj = {
    a: 2
}

var bar = foo.bind(obj);
var b = bar(3);     // 2 3
console.log(b)      // 5

bind方法实际上是返回了一个 包含着 原始函数的硬绑定 的函数,它会把你指定的参数设置为this的上下文对象,并且调用原始函数。

 

 

4、new绑定

例子

function foo() {
    this.f1 = 'test';
}

var b = new foo();
console.log(b.f1);  // test

使用new来调用foo()时,会构造一个新对象,并把它绑定到foo()调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法。

使用new操作符调用一个函数的时候,会执行下面4个操作:

1、创建一个新的对象

2、将构造函数的作用域赋值给新对象(因此,this就指向了这个新对象)

3、运行构造函数的代码

4、返回这个新对象

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值