gpt4 book ai didi

javascript - 创建全局变量与使用JavaScript的构造函数中的变量之间的区别

转载 作者:行者123 更新时间:2023-12-03 11:26:14 24 4
gpt4 key购买 nike

如果我将全局变量x声明为:

var x = "I am window.x";

x将是window对象的公共(public)属性。
如果我调用全局函数(不先使用“call”,“apply”或将其附加到另一个对象),则将窗口对象作为上下文传递(“this”关键字)。
就像将x属性放在当前上下文中一样,该上下文恰好是窗口。

但是,如果我在函数内部以相同的方式声明变量,然后将该函数用作构造函数,则属性x将不是我刚刚构造的对象(当前上下文)的公共(public)属性。我很高兴(我知道我可以做到这一点。x=…),但这似乎有点矛盾。

我是否误解了一些东西(关于它是一种矛盾/不同的行为)?谁能解释发生了什么,还是我必须接受的事情?

希望我的问题清楚。

最佳答案

看来您已经理解得很好(我在下面用您的术语选择了一个小巧的选择)。构造函数内的局部变量就是:构造函数内的局部变量。它们根本不属于由构造函数初始化的实例的一部分。

这都是“范围”在JavaScript中的工作方式的结果。调用函数时,将为该函数调用创建执行上下文(EC)。 EC有一个叫做变量上下文的东西,它有一个绑定(bind)对象(我们称它为“变量对象”,是吗?)。变量对象保存所有var和函数参数以及函数内定义的其他内容。这个变量对象是很真实的东西,对于闭包的工作方式非常重要,但是您不能直接访问它。构造函数中的x是为调用构造函数而创建的变量对象的属性。

所有作用域都有一个变量对象。魔术是全局作用域的变量对象是全局对象,在浏览器中是window。 (更准确地说,window是变量对象上的一个属性,它引用回该变量对象,因此您可以直接引用它。函数调用中的变量对象没有任何等效属性。)因此,您在全局范围内定义的xwindow的属性。

我 promise 的那种挑剔的术语:您已经说过:

If call a global function, the window object will be passed in as the context (the “this” keyword).



多数情况是这样。例如,如果您像这样调用全局函数:
myGlobalFunction();

...然后是的, this将是调用期间的全局对象( window)。但是,您有很多其他方法可以调用该全局函数,而不会使用它。例如,如果您将该“全局”函数分配给对象的属性,然后通过该属性调用该函数,则调用内的 this将是该属性所属的对象:
var obj = {};
obj.foo = myGlobalFunction;
obj.foo(); // `this` is `obj` within the call, not `window`
obj['foo'](); // Exactly the same as above, just different notation

或者您可以使用功能对象的 callapply功能来显式设置 this:
var obj = {};
myGlobalFunction.call(obj, 1, 2, 3); // Again, `this` will be `obj`
myGlobalFunction.apply(obj, [1, 2, 3]); // Same (`call` and `apply` just vary
// in terms of how you pass arguments

需要探索的内容更多(公开:这些是指向我的博客的链接,但其中没有广告或任何内容,似乎不太可能添加它们):
  • Closures are not complicated(探索作用域链和变量对象等)
  • Mythical methods(有关函数和this的更多信息)
  • You must remember this (甚至更多有关函数和this的信息)


  • 更新:您在下面说过:

    I just want to check my understanding: In any scope (global or function) there are always 2 objects: a “this” object (what is that called?) and a “variable object”. In the global scope, these 2 objects are the same. In a function’s scope, they are different, and the “variable object” is not accessible. Is that correct?



    您走在正确的道路上,是的,总是有这两个问题在发生(通常是更多;请参阅下文)。但是“作用域”和 this无关,但与无关。如果您要使用其他语言的JavaScript,这将令人惊讶,但这是事实。 JavaScript中的 this(尽管有时可能会引起误解,有时也称为“上下文”)完全由调用函数的方式定义,而不是由定义函数的位置定义。您可以通过几种方式之一调用函数时设置 this(请参见上面的答案和链接)。从 this的 Angular 来看,定义全局范围的函数与定义在另一个函数内的函数之间没有任何区别。零。齐尔奇

    但是,是的,在JavaScript代码(无论定义在何处)中始终存在 this(可以是任何东西)和变量对象。实际上,通常有多个可变对象按链排列。这称为范围链。当您尝试检索自由变量的值(不合格的符号,例如 x而不是 obj.x)时,解释器会在最上方的变量对象中查找具有该名称的属性。如果找不到,则转到链中的下一个链接(下一个外部作用域),然后查看该变量对象。如果没有,则查看链中的下一个链接,依此类推,依此类推。而且您知道链中的最终链接是什么,对吗?对!全局对象(浏览器上的 window)。

    考虑以下代码(假设我们从全局范围开始; live copy):
    var alpha = "I'm window.alpha";
    var beta = "I'm window.beta";

    // These, of course, reference the globals above
    display("[global] alpha = " + alpha);
    display("[global] beta = " + beta);

    function foo(gamma) {
    var alpha = "I'm alpha in the variable object for the call to `foo`";

    newSection();
    // References `alpha` on the variable object for this call to `foo`
    display("[foo] alpha = " + alpha);
    // References `beta` on `window` (the containing variable object)
    display("[foo] beta = " + beta);
    // References `gamma` on the variable object for this call to `foo`
    display("[foo] gamma = " + gamma);

    setTimeout(callback, 200);

    function callback() {
    var alpha = "I'm alpha in the variable object for the call to `callback`";

    newSection();
    // References `alpha` on the variable obj for this call to `callback`
    display("[callback] alpha = " + alpha);
    // References `beta` on `window` (the outermost variable object)
    display("[callback] beta = " + beta);
    // References `gamma` on the containing variable object (the call to `foo` that created `callback`)
    display("[callback] gamma = " + gamma);
    }
    }

    foo("I'm gamma1, passed as an argument to foo");
    foo("I'm gamma2, passed as an argument to foo");

    function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = msg;
    document.body.appendChild(p);
    }
    function newSection() {
    document.body.appendChild(document.createElement('hr'));
    }

    输出是这样的:
    [global] alpha = I'm window.alpha[global] beta = I'm window.beta--------------------------------------------------------------------------------[foo] alpha = I'm alpha in the variable object for the call to `foo`[foo] beta = I'm window.beta[foo] gamma = I'm gamma1, passed as an argument to foo--------------------------------------------------------------------------------[foo] alpha = I'm alpha in the variable object for the call to `foo`[foo] beta = I'm window.beta[foo] gamma = I'm gamma2, passed as an argument to foo--------------------------------------------------------------------------------[callback] alpha = I'm alpha in the variable object for the call to `callback`[callback] beta = I'm window.beta[callback] gamma = I'm gamma1, passed as an argument to foo--------------------------------------------------------------------------------[callback] alpha = I'm alpha in the variable object for the call to `callback`[callback] beta = I'm window.beta[callback] gamma = I'm gamma2, passed as an argument to foo

    You can see the scope chain at work there. During a call to callback, the chain is (top to bottom):

    • The variable object for that call to callback
    • The variable object for the call to foo that created callback
    • The global object

    Note how the variable object for the call to foo lives on past the end of the foo function (foo returns before callback gets called by setTimeout). That's how closures work. When a function is created (note that a new callback function object is created each time we call foo), it gets an enduring reference to the variable object at the top of the scope chain as of that moment (the whole thing, not just the bits we see it reference). So for a brief moment while we're waiting our two setTimeout calls to happen, we have two variable objects for calls to foo in memory. Note also that arguments to functions behave exactly like vars. Here's the runtime of the above broken down:

    1. The interpreter creates the global scope.
    2. It creates the global object and populates it with its default set of properties (window, Date, String, and all the other "global" symbols you're used to having).
    3. It creates properties on the global object for all var statements at global scope; initially they have the value undefined. So in our case, alpha and beta.
    4. It creates properties on the global object for all function declarations at global scope; initially they have the value undefined. So in our case, foo and my utility functions display and newSection.
    5. It processes each function declaration at global scope (in order, top to bottom):
      • Creates the function object
      • Assigns it a reference to the current variable object (the global object in this case)
      • Assigns the function object to its property on the variable object (again, the global object in this case)
    6. The interpreter begins executing the step-by-step code, at the top.
    7. The first line it reaches is var alpha = "I'm window.alpha";. It's already done the var aspect of this, of course, and so it processes this as a straight assignment.
    8. Same for var beta = ....
    9. It calls display twice (details omitted).
    10. The foo function declaration has already been processed and isn't part of step-by-step code execution at all, so the next line the interpreter reaches is is foo("I'm gamma1, passed as an argument to foo");.
    11. It creates an execution context for the call to foo.
    12. It creates a variable object for this execution context, which for convenience I'll call foo#varobj1.
    13. It assigns foo#varobj1 a copy of foo's reference to the variable object where foo was created (the global object in this case); this is its link to the "scope chain."
    14. The interpreter creates properties on foo#varobj1 for all named function arguments, vars, and function declarations inside foo. So in our case, that's gamma (the argument), alpha (the var), and callback (the declared function). Initially they have the value undefined. (A few other default properties are created here that I won't go into.)
    15. It assigns the properties for the function arguments the values passed to the function.
    16. It processes each function declaration in foo (in order, beginning to end). In our case, that's callback:
      • Creates the function object
      • Assigns that function object a reference to the current variable object (foo#varobj1)
      • Assigns the function object to its property on foo#varobj1
    17. The interpreter begins step-by-step execution of the foo code
    18. It processes the assignment from the var alpha = ... line, giving foo#varobj1.alpha its value.
    19. It looks up the free variable newSection and calls the function (details omitted, we'll go into detail in a moment).
    20. It looks up the free variable alpha:
      • First it looks on foo#varobj1. Since foo#varobj1 has a property with that name, it uses the value of that property.
    21. It looks up display and calls it (details omitted).
    22. It looks up the free variable beta:
      • First it looks on foo#varobj1, but foo#varobj1 doesn't have a property with that name
      • It looks up the next link in the scope chain by querying foo#varobj1 for its reference to the next link
      • It looks on that next link (which happens to be the global object in this case), finds a property by that name, and uses its value
    23. It calls display
    24. It looks up gamma and calls display. This is exactly the same as for alpha above.
    25. It looks up the free variable callback, finding it on foo#varobj1
    26. It looks up the free variable setTimeout, finding it on the global object
    27. It calls setTimeout, passing in the arguments (details omitted)
    28. It returns out of foo. At this point, if nothing had a reference to foo#varobj1, that object could be reclaimed. But since the browser's timer stuff has a reference to the callback function object, and the callback function object has a reference to foo#varobj1, foo#varobj1 lives on until/unless nothing refers to it anymore. This is the key to closures.
    29. Wash/rinse/repeat for the second call to foo, which creates foo#varobj2 and another copy of callback, assigning that second callback a reference to foo#varobj2, and ultimately passing that second callback to setTimeout and returning.
    30. The interpreter runs out of step-by-step code to execute and goes into its event loop, waiting for something to happen
    31. About 200 milliseconds go by
    32. The browser's timer stuff tells the interpreter it needs to call the first callback function we created in foo
    33. The interpreter creates an execution context and associated variable object (callback#varobj1) for the call; it assigns callback#varobj1 a copy of the variable object reference stored on the callback function object (which is, of course, foo#varobj1) so as to establish the scope chain.
    34. It creates a property, alpha, on callback#varobj1
    35. It starts step-by-step execution of callback's code
    36. You know what happens next. It looks up various symbols and calls various functions:
      • Looks up newSection, which it doesn't find on callback#varobj1 and so looks at the next link, foo#varobj1. Not finding it there, it looks at the next link, which is the global object, and finds it.
      • Looks up alpha, which it finds on the topmost variable object, callback#varobj1
      • Looks up beta, which it doesn't find until it gets down to the global object
      • Looks up gamma, which it finds only one link down the scope chain on foo#varobj1
    37. The interpreter returns from the call to callback
    38. Almost certainly, there in its event queue there's a message from the browser waiting for it, telling it to call the second callback function, which we created in our second call to foo.
    39. So it does it all again. This time, the variable object for the call to callback gets a reference to foo#varobj2 because that's what's stored on this particular callback function object. So (amongst other things) it sees the gamma argument we passed to the second call, rather than the first one.
    40. Since the browser has now released its references to the two callback function objects, they and the objects they refer to (including foo#varobj1, foo#varobj2, and anything their properties point to, like gamma's strings) are all eligible for garbage collection.

    Whew That was fun, eh?

    One final point about the above: Note how JavaScript scope is determined entirely by the nesting of the functions in the source code; this is called "lexical scoping." E.g., the call stack doesn't matter for variable resolution (except in terms of when functions get created, because they get a reference to the variable object in scope when they were created), just the nesting in the source code. Consider (live copy):

    var alpha = "I'm window.alpha";

    function foo() {
    var alpha = "I'm alpha in the variable object for the call to `foo`";

    bar();
    }

    function bar() {

    display("alpha = " + alpha);
    }

    foo();

    最终得到 alpha的输出是什么?对! "I'm window.alpha"。即使我们从 alpha调用 foo,我们在 bar中定义的 bar也不会对 foo产生任何影响。让我们快速浏览一下:
  • 设置全局执行上下文等。
  • var和声明的函数创建属性。
  • 为其指定alpha的值。
  • 创建foo函数对象,为其提供对当前变量对象(即全局对象)的引用,并将其放在foo属性上。
  • 创建bar函数对象,为其提供对当前变量对象(即全局对象)的引用,并将其放在bar属性上。
  • 通过创建执行上下文和变量对象来调用foo。变量对象foo#varobj1会获取foo对其父变量对象(当然是全局对象)的引用的副本。
  • 开始逐步执​​行foo的代码。
  • 查找在全局对象上找到的自由变量bar
  • 为对bar及其关联的变量对象bar#varobj1的调用创建执行上下文。给bar#varobj1分配bar对其父变量对象的引用的副本,该对象当然是全局对象。
  • 开始逐步执​​行bar的代码。
  • 查找自由变量alpha:
  • 首先,它看起来在bar#varobj1上,但是那里没有该名称的属性
  • 因此,它查看下一个链接,这是它从全局对象bar获得的链接。因此它找到了全局alpha

  • 注意 foo#varobj1根本不链接到 bar的变量对象。那很好,因为如果范围的定义是由调用函数的方式和位置定义的,我们都会发疯。 :-)一旦理解了它与函数创建有关,而函数创建是由源代码的嵌套所决定的,那么它将变得非常容易理解。

    这就是为什么 bar的范围完全由 bar在源代码中的位置而不是在运行时如何调用决定的原因。

    刚开始您想知道 this和变量分辨率之间的关系就不足为奇了,因为全局对象( window)在JavaScript中有两个不相关的用途:1.如果未以设置函数的方式调用函数,则它是 this的默认值一个不同的对象(在全局范围内),以及2。它是全局变量对象。这些是解释器与全局对象使用的无关的方面,这可能会造成混淆,因为当 this === window时,变量解析似乎与 this有某种联系,但没有。一旦您开始将其他内容用于 thisthis和变量分辨率便会完全断开。

    关于javascript - 创建全局变量与使用JavaScript的构造函数中的变量之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6815775/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com