javascript面向对象 - JavaScript中,作用域,作用域链,执行环境分别是什么时候创建好的,以及函数在定义和调用时发生了什么?
怪我咯
怪我咯 2017-04-11 11:18:56
[JavaScript讨论组]

先抛砖引玉一下:

代码1:

var object = {
    name: "My Object",
    getName: function() {
        return console.log(this.name)//the window
    }
};
(object.getName = object.getName)();//空

代码2:

    var x=10;
    function foo(){
        console.log(x)
    }
    !function(){
        var x=20;
        console.log(x);//20!
        foo();//10!
    }();

    function foo2(){
        var x=30;
        foo();//10;
        !function(){
            console.log(x)//30
        }()
    }
    foo2();

代码3:

var Fn = {};
Fn.method = function(){
    this.name = 'jack';
    function test(){
      console.log(this === window);
    }
    test();
}
Fn.method();//true

问题来了:

1.函数在定义时发生生了什么?
2.函数在不同方式调用时,又发生了什么?
3.作用域,作用域链,执行环境都是在什么时候形成的?
4.如何解释代码2中的结果?
5.函数执行的地方和函数定义的地方有什么样的联系?
6.this指向与作用域,作用域链,执行环境的关系,以及闭包的三个是什么时候创建和有效的?
7.这方面有什么文章或者书籍的章节比较清晰明朗的?

7月1日追加问题

感谢各位大神,现在对整过过程还有点蒙,不知道我理解的对吗,见代码和注释

    var a=1;
    var fn1=function(){
        console.log(this);
    }

    function fn2(arguments){
        var a=1;
        var c=1;
        console.log(a,c);
        function fn1_1(){
            var c1=0;
            console.log(c1);
        }
    }

    function fn3(){
        console.log(this);
        fn2();
    }
    fn3();
/*
    整个解释过程是这样的:

    页面加载;

    创建window全局对象,并生成全局作用域;

    然后生成执行上下文,预解析变量(变量提升),生成全局变量对象;

    然后逐行解析,执行流依次执行每行/块代码;

    直至运行到fn3();

    执行流将fn3压入环境栈中;

    创建fn3的执行环境,创建定义域和定义域链,根据执行上文创建变量对象;

    在创建变量对象的过程中沿定义域链逐级线上搜索变量,并将结果存在函数变量对象中,其中第一活动对象为arguments;

*/
怪我咯
怪我咯

走同样的路,发现不同的人生

全部回复(3)
巴扎黑

1.

函数在定义时,会发生变量提升

例如,

foo();

function foo() {}

会转化为

function foo() {}

foo();

此外,函数的作用域(链)也被创建

例如:


var x = 0;

function foo() {
    var x = 1; // 在 foo 的作用域中定义了 x,所以 foo 的子作用域不能直接访问到全局变量 x
    function bar() {
        x; // 遍历作用域链,在 foo 的作用域中发现了 x,所以 x = 1
    }
}

2.

函数直接调用时,如果在非严格模式,this指针会指向 window,否则为 undefined

当函数以形如 bar.foo() 的形式调用时,this 指针会指向 foo

注意如果中间存在取值操作,则相当于直接调用。
(foo.bar)() 相当于 var bar = bar; bar();

foo.call(that, a, b, c) 让函数就仿佛是以 that.foo(a, b, c) 的形式被调用的,区别在于使用 call 调用时,foo 无需在 that 的原型链上。

foo.apply(that, [a, b, c]) 相当于 foo.call(that, a, b, c)

除此之外还有一种特殊情况

var bar = foo.bind(that); 创建了一个函数 bar,它的 this 指针永远指向 that

箭头函数,如 x => x + 1this 指针永远指向定义箭头函数时定义域中的 this 指针,相当于
foo.bind(this)

3.

ES2015 之前只有函数作用域(链),函数作用域(链)在定义时就形成了

执行环境,顾名思义,在执行时被创建

4.

正确理解 1,2,3 即可解释,不再重复阐述。

5.

正确理解 1,2,3 即可解释,不再重复阐述。

6.

正确理解 1,2,3 即可解释,不再重复阐述。

7.

参见参考资料

参考资料

ES2015 Language Specification

黄舟

写了个简易的近似,模拟自己构造函数(包括作用域,形参数组等)

function myEval(code, that) {
    return new Function(code).call(that);
}

class Fun {
    constructor(scope, parameters, body) {
        this.scope = scope;
        this.parameters = parameters;
        this.body = body;
    }

    apply(that, args) {
        let setupScopeVar = '';
        let scopeKeys = Object.keys(this.scope);
        if (scopeKeys.length > 0)
            setupScopeVar = 'var ' + scopeKeys.map(key => {
                return key + '=' + this.scope[key];
            }).join(',') + ';'

        let setupParam = '';
        if (this.parameters.length > 0)
            setupParam = 'var ' + this.parameters.map((param, i) => {
                if (i < args.length)
                    return param + '=' + args[i];
                else
                    return param;
            }).join(',') + ';'

        let setup = setupScopeVar + setupParam;

        return myEval(setup + this.body, that);
    }
}

let scope = {
    'a': 1
};

let fun = new Fun(scope, ['b', 'c'], 'return a + b + c;');

console.log(fun.apply(null, [2, 3])); // 6

scope.a = 2;

console.log(fun.apply(null, [2, 3])); // 7
迷茫

你问的所有问题的答案都在这里,最精确最权威的解释:Executable Code and Execution Contexts。这是原始文档,网上所有其他解释只能算二手的。

如果想彻底搞懂,请仔细一读。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号