Home  >  Article  >  Web Front-end  >  Understanding and using the concepts of js scope and scope chain_Basic knowledge

Understanding and using the concepts of js scope and scope chain_Basic knowledge

WBOY
WBOYOriginal
2016-05-16 17:37:101105browse

(1) Scope

The scope of a variable is the area of ​​the variable defined in the program source code.

1. Lexical scope is used in JS

Variables that are not declared within any function (var is omitted in a function are also considered global) are called global variables (global scope)
Variables declared within a function have function scope and are local variables

Local variables have higher priority than global variables

var name="one";
function test(){
 var name="two";
 console.log(name); //two
}
test();

Omitting var in the function will affect the global variable, because it has actually been rewritten into a global variable

var name="one";
function test(){
 name="two";
 
}
test();
console.log(name); //two

Function scope, that is to say, function is the basic unit of a scope. js does not have block-level scope like c/c, such as if for, etc.

function test(){
 for(var i=0;i<10;i++){
  if(i==5){
   var name = "one";
  }
 }
 console.log(name); //one
}

test(); //因为是函数级作用域,所以可以访问到name="one"

Of course, higher-order functions are also used in js, which can actually be understood as nested functions

function test1(){
 var name = "one";
 return function (){
  console.log(name);
 }
}
test1()();

After test1(), the outer function will be called, and an inner function will be returned. Then continue(), and the inner function will be called and executed accordingly, so "one"
will be output. Nested functions involve closures, which we will discuss later. Here, the inner function can access the variable name declared in the outer function, which involves the scope chain mechanism

2. Declaration in JS in advance

The function scope in js means that all variables declared within the function are always visible within the function body. Moreover, the variable can be used before it is declared. This situation is called hoisting
tip: Declaration in advance is performed when the js engine is pre-compiled. The phenomenon of declaration in advance occurs before the code is executed

For example

var name="one";
function test(){
 console.log(name); //undefined
 var name="two";
 console.log(name); //two
}

test();

The above achieves the following effect

var name="one";
function test(){
 var name;
 console.log(name); //undefined
 name="two";
 console.log(name); //two
}

test();

Try removing var again? This is the name within the function that has become a global variable, so it is no longer undefined

var name="one";
function test(){
 console.log(name); //one
 name="two";
 console.log(name); //two
}

test();

3. It is worth noting that none of the above mentioned parameters are passed. What if test has parameters?

function test(name){
 console.log(name); //one
 name="two";
 console.log(name); //two
}

var name = "one";
test(name);
console.log(name); // one

As mentioned before, basic types are passed by value, so the name passed into the test is actually just a copy. This copy is cleared after the function returns.
Don’t think that name="two" in the function changes the global name, because they are two independent names

(2) Scope chain

The advanced functions mentioned above involve scope chain

function test1(){
 var name = "one";
 return function (){
  console.log(name);
 }
}
test1()();

1. Introduce a large paragraph to explain:
Each piece of js code (global code or function) has a scope chain associated with it.

This scope chain is a list or linked list of objects. This group of objects defines the variables "in scope" in this code.

When js needs to find the value of variable x (this process is called variable resolution), it will start from the first object in the chain. If this object has an attribute named x, then The value of this attribute will be used directly. If there is no attribute named x in the first object, js will continue to search for the next object in the chain. If the second object still does not have an attribute named x, it will continue to look for the next one, and so on. If no object in the scope chain contains attribute x, then it is considered that x does not exist in the scope chain of this code, and eventually a ReferenceError exception is thrown.

2. Scope chain example:

In the top-level code of js (that is, the code that does not include any function definition), the scope chain consists of a global object.

In a function body that does not contain nesting, there are two objects on the scope chain. The first is the object that defines function parameters and local variables, and the second is the global object.

In a nested function body, there are at least three objects in the scope.

3. Scope chain creation rules:

When a function is defined (note, it starts when it is defined), it actually saves a scope chain.

When this function is called, it creates a new object to store its parameters or local variables, adds the object to that scope chain, and creates a new, longer representation of the function calling scope. The "chain".

For nested functions, the situation changes again: every time the external function is called, the internal function will be redefined again. Because every time an external function is called, the scope chain is different. Inner functions need to be subtly different each time they are defined - the code of the inner function is the same every time the outer function is called, and the scope chain associated with this code is also different.

(tip: Understand the above three points well and remember it. It is best to say it in your own words, otherwise you will have to memorize it, because the interviewer will ask you directly: Please describe the scope chain... )

A practical example of scope chaining:

var name="one";
function test(){
 var name="two";
 function test1(){
  var name="three";
  console.log(name); //three
 }
 function test2(){
  console.log(name); // two
 }
 
 test1();
 test2();
}

test();

上边是个嵌套函数,相应的应该是作用域链上有三个对象
那么在调用的时候,需要查找name的值,就在作用域链上查找

当成功调用test1()的时候,顺序为 test1()->test()->全局对象window 因为在test1()上就找到了name的值three,所以完成搜索返回
当成功调用test1()的时候,顺序为 test2()->test()->全局对象window 因为在test2()上没找到name的值,所以找test()中的,找到了name的值two,就完成搜索返回

还有一个例子有时候我们会犯错的,面试的时候也经常被骗到。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
function buttonInit(){
  for(var i=1;i<4;i++){
    var b=document.getElementById("button"+i);
    b.addEventListener("click",function(){ 
      alert("Button"+i); //都是 Button4
    },false);
  }
}
window.onload=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>

为什么?
根据作用域链中变量的寻找规则:

b.addEventListener("click",function(){ 
      alert("Button"+i);
    },false);

这里有一个函数,它是匿名函数,既然是函数,那就在作用域链上具有一个对象,这个函数里边使用到了变量i,它自然会在作用域上寻找它。
查找顺序是 这个匿名函数 -->外部的函数buttonInit() -->全局对象window

匿名函数中找不到i,自然跑到了buttonInit(), ok,在for中找到了,

这时注册事件已经结束了,不要以为它会一个一个把i放下来,因为函数作用域之内的变量对作用域内是一直可见的,就是说会保持到最后的状态

当匿名函数要使用i的时候,注册事件完了,i已经变成了4,所以都是Button4

那怎么解决呢?

给它传值进去吧,每次循环时,再使用一个匿名函数,把for里边的i传进去,匿名函数的规则如代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript">
function buttonInit(){
  for(var i=1;i<4;i++){
    (function(data_i){
    var b=document.getElementById("button"+data_i);
    b.addEventListener("click",function(){ 
      alert("Button"+data_i);
    },false);
    })(i);
  }
}
window.onload=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>

这样就可以 Button1..2..3了

4.上述就是作用域链的基本描述,另外,with语句可用于临时拓展作用域链(不推荐使用with)

语法形如:

with(object)

statement

这个with语句,将object添加到作用域链的头部,然后执行statement,最后把作用域链恢复到原始状态

简单用法:

比如给表单中各个项的值value赋值

一般可以我们直接这样

var f = document.forms[0];
f.name.value = "";
f.age.value = "";
f.email.value = "";

引入with后(因为使用with会产生一系列问题,所以还是使用上面那张形式吧)

with(document.forms[0]){
f.name.value = "";
f.age.value = "";
f.email.value = "";
}

另外,假如 一个对象o具有x属性,o.x = 1;
那么使用

with(o){
 x = 2;
}

就可以转换成 o.x = 2;
假如o没有定义属性x,它的功能就只是相当于 x = 2; 一个全局变量罢了。

因为with提供了一种读取o的属性的快捷方式,但他并不能创建o本身没有的属性。

要理解变量的作用域范围就得先理解作用域链
用var关键字声明一个变量时,就是为该变量所在的对象添加了一个属性。
作用域链:由于js的变量都是对象的属性,而该对象可能又是其它对象的属性,而所有的对象都是window对象的属性,所以这些对象的关系可以看作是一条链
链头就是变量所处的对象,链尾就是window对象

看下面的代码:

复制代码 代码如下:

function t() {
var a;
function t2() {
var b;
}
}

js中函数也是对象,所以变量a所在的对象是t,t又在window对象中,所以a的作用域链如下
t--window
那么b所以在的对象即t2,t2又包含在t中,t又在window对象,所以b的作用域链如下
t2--t--window
明白了作用域链下面就开始变量的作用域分析了
1 javascript 没有var的变量都为全局变量,且为window对象的属性
复制代码 代码如下:

function test1() {
//执行这个句的时候它会找作用域对象,这个函数就是作用域链中的第一个对象,但这个对象中没有相关的var语句
//于里就找作用域链的第二个对象,即全局对象,而全局对象中也没有相关的var语句
//由于没有相关的var语句,js隐式在函数地声明了变量即var all;
all = 30;
alert(all);
}
test1();
alert(all);
alert(window.all);

2 Variables defined within a function (except functions within a function) are valid within the entire function
Copy code The code is as follows:

function test2() {
var t = 0;
//Define variables in the condition of for. The scope chain object of this change is this function
//So it is valid in the entire function
for (var i = 0; i < 5; i ) {
t = i;
}
alert(i);
}
test2();

3 Variables inside the function replace global variables with the same name
Copy code The code is as follows:

var t = "bb";
function test() {
//When executing t, it will first find the scope chain object. Since it is defined inside the function, this function is the first object of its scope chain
//And there is a definition of t in this object, so t is a local variable, which replaces the global variable t
//t is only defined at this time, but there is no assignment. The assignment is on the next line, so undefined is output here
alert(t);
var t = "aa";
alert(t);
}
test();

4 No block scope
Copy code The code is as follows:

if (true) {
//A variable is defined in the block, and the first object of its scope chain is the global object window
var tmp = 0;
}
//The first object of the scope chain of tmp is the global object window, and there are related var statements in the global object above, so the output is 0
alert(tmp);


The following content comes from the summary of reading online blogs. Use it as notes and only remember the key points. At the same time, I am very grateful to the bloggers who are willing to share. It is you who let me stand on the shoulders of giants!
1.
Copy code The code is as follows:

var temp = (function(){
var name="test";
return function(){
alert(name);
}
})();

The above code snippet is a writing method we often see in jser, and it is the legendary closure. As we all know: calling temp(); will pop up "test"; this process can be explained based on the following three theories:

1) The js scope is only related to the delimiter of the function, and the nesting of functions forms a scope chain;
2) The creation rule of the scope chain is to copy the scope chain of the previous environment and put the pointer to the environment variable object at the head of the chain;
3) In Javascript, if an object is no longer referenced, the object will be recycled by GC. If two objects refer to each other and are no longer referenced by a third party, then the two objects that refer to each other will also be recycled.

If you still don’t understand after reading the above 3 items, you can read the following detailed explanation of the code based on theory:
First, the outer function is destroyed after execution; however, the scope chain of the outer function is copied to the scope chain of the inner function, forming part of the scope chain of the inner function. Remember, it is a copy, not a reference (according to Article 2), so the inner function can still access name; since the returned inner function is referenced by temp, when the outer function is destroyed after execution, the inner function still exists although it is part of the outer function. , just like the 3rd basis, it was quoted by a third party; the legendary closure is also this principle
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn