杂七杂八的前端基础01——函数作用域

127 阅读2分钟

首先我们来声明一个js对象person:

var person = {
    talk() {
        console.log("I am talking...");
    }
};

然后调用person对象的talk方法:

person.talk();

结果如下:

没有任何问题,然后我们将person对象的talk方法赋值给一个全局变量talk,并调用这个全局的talk方法:

var talk = person.talk;
talk();

结果如下:

和之前的执行结果相同。

现在我们来给person对象新增属性name和方法sayName,修改之后的person对象如下:

var person = {
    name: 'xiaoming',
    talk() {
        console.log("I am talking...");
    },
    sayName() {
        console.log("My name is " + this.name);
    }
}

调用person对象的sayName方法:

person.sayName();

结果如下:

没有任何问题,输出的name是person对象的name属性,然后我们将person对象的sayName方法赋值给一个全局变sayName,并调用这个全局的sayName方法:

var sayName = person.sayName;
sayName();

结果如下:

我们发现此时没有找到name,输出undefined,这是由于我们调用sayName方法时,是在全局作用域下调用的该方法,对比一下:

//调用person对象的sayName方法:
person.sayName();
调用全局的sayName函数,浏览器环境下相当于调用:
window.sayName();

而此时顶级对象window下并没有name属性,所以输出undefined

这里多提几句,之前关于this的分析全是基于浏览器运行环境下的,如果在node下声明函数,this指向的是global对象,而在该node模块中声明的全局变量属于该模块,不属于global对象,如果想要将全局变量绑定到global,那么需要在不使用var或者let等关键词,就可以挂载到global上了。

我们再拓展一下,如果在定时器中使用this是指向什么作用域呢?、 在person对象中新增一个方法sayNameLater,修改之后的person对象如下:

var person = {
    name: 'xiaoming',
    talk() {
        console.log("I am talking...");
    },
    sayName() {
        console.log("My name is " + this.name);
    },
    sayNameLater() {
        setTimeout(function() {
            console.log("My name is " + this.name);
        },1000);
    }
}

调用person对象的sayNameLater方法:

person.sayNameLater();

结果如下:

我们发现此时没有找到name,输出undefined,这是由于我们在使用定时器时,函数中的this会指向window对象(浏览器环境下),如果我们此时声明全局变量name并赋值:

var name = "xiaohong";

再调用person对象的sayNameLater方法:

person.sayNameLater();

结果如下:

使用定时器时,函数中的this会指向window对象,即使我们使用对象方法而不是函数,this依然会指向window对象。现在person对象不变,全局变量name不变,在定时器中调用person对象的sayName方法:

var name = "xiaohong";
var person = {
    name: 'xiaoming',
    talk() {
        console.log("I am talking...");
    },
    sayName() {
        console.log("My name is " + this.name);
    },
    sayNameLater() {
        setTimeout(function() {
            console.log("My name is " + this.name);
        },1000);
    }
}
setTimeout(person.sayName, 1000);

结果如下:

可以看到使用的仍然是全局变量name,这是由于计时器在解析第一个参数的时候使用eval执行,相当于我们最开始说的,将函数的方法赋值给一个变量,并执行该变量方法,所以作用域依然是全局。