js变量作用域,变量提升和函数传参
参考:
https://blog.csdn.net/alokka/article/details/88532347 (全局作用域,局部作用域,块作用域)
https://blog.csdn.net/qq_39712029/article/details/80951958 (变量提升)
全局作用域
1,用 var 在全局(函数外)声明的所有变量,都具有全局作用域,即: 网页中所有脚本和函数均可使用。
var carName = " Volvo"; // 此处可调用 carName 变量 function myFunction() { // 函数内可调用 carName 变量 }
2,没有声明的变量(没有使用var)会自动提升到全局作用域
如果是在函数里面,需要执行函数才能生效,并且也只有在函数执行后面的作用域才能使用
//情形1 console.log(op1);//会报错 op1='ddd'; console.log(op1); //情形2 console.log(op2);//undefined var op2='ddd';//因为变量有提升 console.log(op2);//ddd //情形3 console.log(carName)//会报错 function myFunction() { carName = "Volvo"; // 此处可调用 carName 变量 } //需要执行myFunction后才能使用
局部作用域(即函数作用域别去追究)
1,函数内部什么定义的变量,只在函数内使用;
2,生命周期:在函数调用时临时生成,调用结束后就释放;
块作用域
JS中作用域有:全局作用域、函数作用域,没有块作用域的概念。
支持块级作用域的编程语言:java,c++,c
不支持块级作用域的编程语言:javascript,php,python
for(var i = 0; i < 4; i++) { var d = 5; }; console.log(i); // 4 (循环结束i已经是4,所以此处i为4) console.log(d); // 5
但是es6新增了块作用域,即使用let和const实现块作用域;
for(let i = 0; i < 4; i++) { const d=i; console.log(d);//0,1,2,3,说明每次循环体内,都是一次新的作用域 }; console.log(i); //会报错 console.log(d); //会报错
变量提升
1,js代码自上而下执行之前,浏览器首先会把当前上下文中所有带“var / function”关键字进行提前的声明和定义,解析到它们对应作用域开始的位置,这种预先处理的机制叫做变量提升,变量提升的意义在于创建变量前使用这个变量不报错。
2,变量提升也可以称之为预解析。可以理解为这是词法解析的一个环节,语法解析发生在代码执行前;
3,JavaScript 仅提升声明,而不提升初始化;
4,函数和变量相比,会被优先提升
变量提升
我们习惯将var a = 2;看做是一个声明,而实际上javascript引擎并不这么认为。它将var a和a = 2看做是两个单独的声明,第一个是编译阶段的任务,而第二个则是执行阶段的任务。
例子1:
console.log(v1); var v1 = 100; function foo() { console.log(v1); var v1 = 200; console.log(v1); } foo(); console.log(v1);
//结果 //undefined //undefined //200 //100
实例2:
var name = "world"; (function(){ if(typeof name === "undefined") { var name = "Jack"; console.log("Hello " + name); } else { console.log("Hello " + name); } }()); //解析:相当于 var name = "world"; (function(){ var name; // 变量提升,仅提升声明,不提升初始化 if(typeof name === "undefined") { name = "Jack"; console.log("Hello " + name); } else { console.log("Hello " + name); } }());
函数提升
javascript中不仅仅是变量声明有提升的现象,函数的声明也是一样;具名函数的声明有两种方式:
//函数声明式,函数提升是整个代码块(相当于申明和初始化都提升了)提升到它所在的作用域的最开始执行 function bar () {}
//函数字面量式,这种情况和变量提升是一样的 var foo = function () {}
实例1
foo(); //1 ,因为函数提升比变量提升更优先 var foo; function foo () { console.log(1); } foo = function () { console.log(2); }
变量的值传递和引用传递
// 基本类型 var num1 = 5; var num2 = num1; num2 = 10; console.log(num1 + ' | ' + num2); // 5 | 10 // 引用类型 var obj1 = new Object(); obj1.num = 5; var obj2 = obj1; obj2.num = 10; console.log(obj1.num + ' | ' + obj2.num); // 10 | 10
一句话,引用类型复制的是指针的指向。
函数传参
一 ,参数
1, 所有的参数传递,都是传递值的拷贝。(如果想知道为什么,去学习编译原理的函数调用的参数压栈和出栈对应内容)。
2 ,C传指针进去,其实也是把这个指针值按拷贝传送进去。但是因为指针值指向一块外部内存空间(其实更多是堆空间,或外层栈变量空间),所以感觉可以在函数里改变外部变量。其实本质还是按拷贝传递,只是传递进去的是一个访问变量的渠道。
因此,如果我们希望函数内能改变外部的指针值,往往传进去的是指针变量的指针。呵呵,很多初学C的程序员,对**非常难理解。
二 ,返回值
返回值是按拷贝传递,函数出栈后,会传出一个值,该值在调用函数的代码段的生命周期里一直有效。相当与调用点形成一个匿名的栈变量。
变量a = function(); 而a并不等于函数里return的那个值。
其实function()执行结果自身就是一个匿名变量。(其实编译器会检查语法,如上面a=function这样的语法,匿名变量不会生成,直接使用a变量拷贝返回值)
例如: function()返回int值。 完全可以 int x = function() + 6;//注意:+运算时,函数已经执行完毕,所有函数出栈操作已经结束。
很明显function()必须有一个变量或常量参与计算,而函数里return的值会随函数调用结束出栈而被删除,所以必须拷贝构造传递出来。
上代码:
var a = {
num:'1'
};
var b = {
num:'2'
};
function change(obj){
//obj.num = '3';//
obj = {
num:'3'
};
return obj.num;
}
var result = change(a);
console.log(result + ' | ' + a.num); // 2 | 3