深入理解javascript作用域和上下文环境

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ws9029/article/details/88366278

1.上下文和作用域的概念

首先,上下文和作用域是两个不同的概念,多年来很多开发者会混淆这两个概念(我自己也是),函数调用和作用域和上下文紧密相关,作用域是对于函数而言的(除了全局作用域),只有函数才会创建作用域,函数定义的时候作用域就确定好了,无论你调用不调用,你只要创建了函数,它就会有个单独作用域,一个属于自己的地盘,而上下文是对于对象而言的,简单的说作用域涉及到被调用的函数中的变量的访问,上下文始终是this的值,这个是由函数运行时决定的,简单来说就是谁调用此函数,this就指向谁,就有一个这个函数的上下文,一个作用域下可能会有多个上下文(函数被调用多次),也可能没有(函数未调用过),当函数调用完成,上下文环境将被摧毁。看下面的例子:

var x = 100;
//全局作用域
function fn(x) {
//fn 作用域
console.log(x,this);//5,window
var obj = {
    fn1:function(){
        // fn1作用域
        var n=10;
        console.log(n,this==obj) //10,true
    }
}
obj.fn1()
};
fn(5);

这里有3个作用域,一个全局作用域,fn作用域,fn1作用域,刚一开始全局作用域中有x、fn函数,当执行fn(5)时候,会创建一个上下文执行环境,此时的上下文是window, 此时fn作用域中包括x=5, obj对象,然后执行obj.fn1();又创建了一个上下文,因为调用者是obj,所以这个上下文环境是obj对象,此环境中 fn1作用域包括 n=10, 所以全局作用域——fn作用域——fn1作用域就形成了一个作用域链式。(可能这个例子不太具体)。我稍微改下例子:

var x = 100;
//全局作用域
function fn(x) {
//fn 作用域
console.log(x,this);//5,window
var n=10;
function fn1(){
        // fn1作用域
        console.log(n,this)//10,window
    }
fn1()
};
fn(5);
fn(10)

调用2次fn,所以在fn作用域中产生了2次上下文,因为函数自调用所以this都是指向window。作用域的访问原则,子可以访问父及作用域的变量,父级不能访问子的作用域的变量,这是个不可逆的访问,当调用fn1时候,此时fn1 作用域下没有n,所以它会向上级访问到n=10;

2.js作用域(ES5)

1.全局变量:声明在函数外部的变量,在代码中任何地方都能访问到的对象拥有全局作用域(所有没有var直接赋值的变量都属于全局变量)
2.局部变量:声明在函数内部的变量,和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,所有在一些地方也会看到有人把这种作用域称为函数作用域(所有没有var直接赋值的变量都属于全局变量)

1.最外层函数和在最外层函数外面定义的变量拥有全局作用域
var a = 10;
function fn() {
    console.log(a);
};
fn(); //全局变量,所以可以直接访问到 结果为10
function fn() {
    var a = 10;
};
fn();
consoele.log(a); //a 为函数fn()内部变量量即局部变量,所以无法访问到
2.所有末定义直接赋值的变量自动声明为拥有全局作用域
function fn() {
    a = 10;
}
fn();
console.log(a); //结果为10; 
//等价于:
var a;
function fn() {
    a = 10;
};
fn();
console.log(a);
function fn() {
    var a = b = 10;
}
fn();
console.log(a); 
console.log(b); 
//等价于
function fn() {
    b = 10; //b为全局变量
    var a = b;//a为fn函数的局部变量
}
fn();
console.log(a); //结果为,无法访问到
console.log(b); //结果为10;
3.变量的查找是就近原则去寻找,变量的声明会被提前到作用域顶部
var a = 10;
function fn() {
    alert(a);
    var a = 20;
}
fn(); 
//等价于
var a = 10;
function fn() {
    var a; //声明提前了
    alert(a);//结果为:undefined
    a = 20; //这里才赋值
}
fn();
var a = 10;
function fn(b) {
    alert(b);
    var b = 20; 
}
fn(a); 
//等价于
var a = 10;
function fn(b) {
    var b =a; // 形参b是a的值 这样默认会var b = a =10; 
    alert(b);
    var a = 20; 
}
fn(a); //结果为:10
4.js词法作用域

词法作用域就是定义此法阶段的作用域,即你写代码时将变量和作用域写在哪里而决定其作用范围,无论函数在哪里被调用,以何种方式调用,其词法作用域都只由被声明时所处的位置决定,即你写下哪他就在哪发挥作用。

var a = 10;
function fn() {
    console.log(a);// a的值为全局变量10
};
function fn1() {
    var a = 20;
    fn();//10
}
fn1(); //当创建这个函数时候作用域就已经定义好了 所以fn()不会因为位置变化而改变它的值
2.上下文环境
1.当函数作为对象的方法被调用时,this 指向调用方法的对象。
var obj={
    fn:function(){
        console.log(this)//obj
    }
}
obj.fn();
2.当调用一个函数时,通过 new 操作符创建一个对象的实例,当以这种方式调用时,this 指向新创建的实例,当调用一个未绑定函数,this 默认指向全局上下文或者浏览器中的window对象。
function Constructor(){
    console.log(this);
}
Constructor();//window
var p =new Constructor();// p
3.改变上下文环境(call,apply)
var number = 100;
function Fn1() {
    this.number = 10 ;
}
function Fn2() {
    console.log(this.number);
}
Fn2();//this 指向window                                                   100
Fn2.call(window); //  把this指向window  等价于 window.number               100 
Fn2.call(new Fn1());// 把this指向Fn1的实例  等价于输出 (new Fn1()).number   10
Fn2.apply(window); //  把this指向window  等价于 window.number              100
Fn2.apply(new Fn1());// 把this指向Fn1的实例  等价于输出 (new Fn1()).number  10

最后说下是在javascript中上下文是怎么执行?

当javascript代码文件被浏览器载入后,默认最先进入的是一个全局的执行上下文。当在全局上下文中调用执行一个函数时,程序流就进入该被调用函数内,此时引擎就会为该函数创建一个新的执行上下文,并且将其压入到执行栈顶部(作用域链)。浏览器总是执行位于执行栈顶部的当前执行上下文,一旦执行完毕,该执行上下文就会从执行栈顶部弹出,并且控制权将进入其下的执行上下文。这样,执行栈中的执行上下文就会被依次执行并且弹出,直到回到全局的执行上下文

猜你喜欢

转载自blog.csdn.net/ws9029/article/details/88366278