this 总结(上偏)

191 阅读3分钟

1.1 this由来

如下代码,输出对象you 、me的name值

在没有this的情况下:

var you = {
    name: 'Kyle'
}
var me = {
    name: 'reader'
}
function identify(context){
    return context.name.toUperCase();
}
function speak(context){
    var greeting="hello i'm" + idenify(context);
    console.log(greeting);
}
identify(you); //Kyle
speak(me); // hello i'm reader

在使用this的情况下:

var you = {name:'reader'}
var me = {name:'kyle'}
function identify(){return this.name.toUpperCase();}
function speak(){consol.log("hello i'm" + identify.call(this)}
identify.call(me)
speack.call(me)

通过对比我们不难发现,在使用this时,this提供了一种更优雅的方式来隐式“传递”一个对象引用,因此可以将API设计的更加简介且易于复用。

随着模块越来越复杂,显式传递上下文对象会让代码变得越来越混乱,当我们使用原型继承时可以自动引用合适的上下文对象是多么的重要。

1.2 误解

有两种常见的对于this的误解

1.2.1  指向自身

JavaScript开发者通常认为,既然可以把函数看作一个对象,那就可以在函数调用时存储状态,这是可行的,有时候也确实有用,不过除函数对象外还有很多更合适存储状态的地方。

如下错误代码示例

function foo(num){
    console.log(num);
    this.count++
}
foo.count=0
for(var i=5;i<10;i++){
    foo(i)
}
// 5
// 6
// 7
// 8
// 9
console.log(foo.count) //0

console.log()产生了5条数据,证明函数foo确实执行了五次,然而foo.count依然是0,可见this并不是指向自身。

执行foo.count=0,的确向函数对象foo中添加了一个属性count,但是函数内部代码this.count中的this并不是指向的那个函数对象,虽然属性名相同,但是根对象却不相同。

就在函数内部访问函数本身而言,应使用如下代码:

function foo(number){
    console.log(number)
    foo.count++
}
foo.count=0
for(var i=5;i<10;i++){
    foo(i);
}
// 5
// 6
// 7
// 8
// 9
console.log(foo.count) // 5

尽管我们规避了this指向错误,但是我们并没有搞清楚this指向了哪里

1.2.2  this的作用域

第二种常见的错误是,this指向函数的作用域。这个问题有点烧脑,因为某种情况下他是正确的,但是其他情况下他是错误的。

需要明确的是,this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性,但是作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript引擎内部。

如下代码,它试图使用this来隐式引用函数的词法作用域:

function foo(){
    var a = 2;
    this.fn()
}
function fn(){
    console.log(this.a)
}
foo()// referenceError: a is not defined

首先通过this来访问fn并成功纯属意外,最自然的方式是省略this。

次代码试图使用this来联通foo和fn的词法作用域,从而让fn可以访问foo作用域中的a,这是不可能实现的。

1.3 this到底是什么

this是在函数运行时绑定的,并不是编写是绑定的。它的上下文取决于函数调用的各种条件。

当一个函数被调用时,会创建一个活动记录(亦称执行上下文),这个记录会包含函数在哪里被调用(调用栈),函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到


搬自《你不晓得的JavaScript》上