推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

为什么要用this指向确定函数的执行上下文对象

   2023-06-08 网络整理佚名1340
核心提示:this既不是自身也不是当前函数的作用域。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息,this也是这里的一个属性。foo的地址,这个地址指向的是一个函数,也就是说bar的调用其实符合“独立函数调用”规则。我们看到,回调函数虽然是通过obj引用的,但是this也不是obj了。其中,第三步绑定了this,所以“构造函数”和原型中的this永远指向new出来的实例。

为什么要用这个

要回答这个问题,我们先看看不使用这个会出现什么问题。 试想一下,如果不使用这个,下面的代码应该怎么写:

function speak(){
    var name = this.name
    console.log(`Hello I am ${name}`)
}
var me = {
    name: 'a',
    speak: speak
}
var you = {
    name: 'b',
    speak: speak
}
me.speak()  //Hello I am a
you.speak()  //Hello I am b
复制代码

这可以在同一执行环境中使用不同的上下文对象。 它实际上提供了一种更优雅的方式来隐式“传递”对象引用,因此可以使 API 设计更加简洁,易于重用。

这是谁

这既不是它本身,也不是当前功能的范围。 我们可以用代码来测试一下。

function fn(){
    console.log(this.name)
}
fn.name = 'xxx'
fn()  //undefined
复制代码

function foo() { 
    var a = 2;
    this.bar(); 
}
function bar() { 
    console.log( this.a );
}
foo(); //undefined
复制代码

那么这是谁? 不一定,this是运行时绑定的,所以要看函数的执行上下文。

调用函数时,会创建活动记录(执行上下文)。 这条记录会包含函数调用位置(调用栈)、函数的调用方式、传入的参数等信息,这也是这里的一个属性。

如何判断这个点

判断this的重点就是判断函数的执行上下文,即“谁调用了它”。 有几种判断方法:

独立函数调用

function foo(){
    console.log(this.a)
}
var a = 2
foo()  // 2
复制代码

这个直接调用this的方法指向全局对象,如果在浏览器中,则指向

对象上下文(隐式绑定)

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

尽管 foo 是在全局范围内定义的,但在调用时会被 obj 上下文引用。 可以理解为在调用foo的那一刻是属于obj对象的。 所以这指向 obj。

这里有两个问题:

在链式调用的情况下,只有最后一层会影响调用位置,例如:

obj1.obj2.obj3.fn() //这里的fn中的this指向obj3
复制代码

function foo() { 
    console.log( this.a );
}
var obj = { 
    a: 2,
    foo: foo 
};
var bar = obj.foo; // 函数别名!
var a = "xxxxx"
bar(); // xxxxx
复制代码

这里的bar其实是指obj.foo的地址,指向一个函数,也就是说bar的调用其实是符合“函数独立调用”规则的。 所以这不是 obj。

回调函数实际上是隐式丢失的

稍微改变一下上面的代码:

function foo() { 
    console.log( this.a );
}
var obj = { 
    a: 2,
    foo: foo 
};
var a = "xxxxx"
setTimeout( obj.foo ,100); // xxxxx
复制代码

我们看到回调函数虽然被obj引用了,但是this已经不是obj了。 其实内置的()函数的实现类似于下面的伪代码:

function setTimeout(fn, delay){
    //等待delay毫秒
    fn()
}
复制代码

实际上,这段代码将这个操作隐藏为fn=obj.foo,类似于上例中的bar=obj.foo。

显式绑定

显式绑定与隐式绑定相反,隐式绑定是指通过call、apply、bind显式改变this的方向。

这三个方法的第一个参数就是this要指向的对象。

请注意,如果您将值(字符串、布尔值、数字)类型传递给第一个参数,该值将被转换为对象形式(调用 new(..)、new(..)、new(..) )。

这三个方法中的bind方法比较特殊,它可以延迟方法的执行,让我们写出更灵活的代码。 它的原理也很容易模拟:

function foo(something) { 
    console.log( this.a, something ); 
    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 ); // 2 3 
console.log( b ); // 5
复制代码

注意:如果第一个参数传入null or,这个值会被忽略,相当于符合独立函数调用的规则

新绑定

Js中的new不同于传统的面向类的语言机制,Js中的“构造函数”其实和普通的函数没什么区别。

实际上,当我们使用new来调用一个函数时,会发生以下事情:

其中,第三步绑定了this,所以“构造函数”和原型中的this总是指向新的实例。

优先事项

以上四个判断规则的权重是递增的。 判断顺序是:

严格模式的区别

上面说的都是在非严格模式下建立的,在严格模式下这一点是不一样的。

箭头函数中的this

箭头函数不是通过关键字定义的,也没有使用上面的this规则,而是“继承”了外作用域中的this指针。

其实,虽然没有箭头函数,但是我们经常做一些和箭头函数效果一样的事情,例如:

function foo() {
    var self = this; 
    setTimeout( function(){
        console.log( self );
    }, 100 );
}
复制代码

有了这个

es6 中的 or 函数会将 this 绑定到设置或获取属性的对象上。

function sum() {
  return this.a + this.b + this.c;
}
var o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return (this.a + this.b + this.c) / 3;
  }
};
Object.defineProperty(o, 'sum', { get: sum, enumerable: true, configurable: true} );
console.log(o.average, o.sum); // logs 2, 6

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON