hoisting机制:javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面。
知识点一:javascript是没有块级作用域的。函数是JavaScript中唯一拥有自身作用域的结构
知识点二:变量声明宣称一个名字的存在,变量定义则为这个名字分配存储空间,而变量初始化则是为名字分配的存储空间赋初值
知识点三:javascript中一个名字(name)以四种方式进入作用域(scope),其优先级顺序如下:
1、语言内置:所有的作用域中都有 this 和 arguments 关键字
2、形式参数:函数的参数在函数作用域中都是有效的
3、函数声明:形如function foo() {}
4、变量声明:形如var bar;
知识点四:1、函数参数是原始类型的值(数值、字符串、布尔值),传递方式是传值传递,即在函数体内修改参数值,不会影响到函数外部。
只提升变量名
,不提升函数值
!我是函数声明,所以我全部被提升了
,包括函数名和函数体。另外,我的优先级比变量声明要高,名字和我相同的变量声明会被忽略! 解析:(1)、变量a,f,b,c的声明会被提升到函数作用域的最前面,类似如下:
(function(){
var a,f,b,c;
a = "1";
f = function(){};
b = "2";
c = "3";
})();
(2)、请注意函数表达式并没有被提升,这也是函数表达式与函数声明的区别。进一步看二者的区别:
(function(){
//var f1,function f2(){}; //hoisting,被隐式提升的声明
f1(); //ReferenceError: f1 is not defined
f2();
var f1 = function(){};
function f2(){console.log('111')}
})();
上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明 是被提升的。
二、变量声明提升优先级实例加解说(帮助更好理解)(证明知识点三)
1、eg:(function(){
var foo;
console.log(typeof foo); //function
function foo(){}
foo = "foo";
console.log(typeof foo); //string
})();
解析:(1)、一个变量的名字与函数的名字相同,那么函数的名字会覆盖变量的名字,无论其在代码中的顺序如何。但名字的初始化却是按其在代码中书写的顺序进行的,不受以上 优 先级的影响。如果形式参数中有多个同名变量,那么最后一个同名参数会覆盖其他同名参数,即使最后一个同名参数并没有定义。知识点三名字解析优先级存在例外,比如 可以覆盖语言内置的名字arguments。
2、命名函数表达式:可以像函数声明一样为函数表达式指定一个名字,但这并不会使函数表达式成为函数声明。命名函数表达式的名字不会进入名字空间,也不会被提升。
eg:(function(){
f();//TypeError: f is not a function
foo();//ReferenceError: foo is not defined
var f = function foo(){console.log(typeof foo);};
f();//function
foo();//ReferenceError: foo is not defined
})();
解析:命名函数表达式的名字只在该函数的作用域内部有效。
3、eg:var myval = "my global var";
(function() {
console.log(myval); // "my global var"
})();
解析:将上面的例子稍作修改:
var myval = "my global var";
(function() {
console.log(myval); //"undefined"
var myval = "my local var";
})();
执行结果是输出了一个 undefined,出现这个结果的原因就是变量的声明被提升了,以上代码等同如下:
var myval = "my global var";
(function() {
var myval;
console.log(myval); //"undefined"
myval = "my local var";
})();
被提升的仅仅是变量的声明部分,并没有立即初始化,所以会输出 undefined。
4、例子3的这种提升机制,不仅仅表现于在普通的变量,同时也表现在函数上。eg:
(function() {
fun(); // Uncaught TypeError: undefined is not a function
var fun = function() {
console.log("Hello!");
}
})();
解析:上面的例子等价于:
(function() {
var fun;
fun(); // Uncaught TypeError: undefined is not a function
fun = function() {
console.log("Hello!");
}
})();
因为函数的声明同样被提升而没有立即初始化,所以会出错。
当然,这种定义函数的方式称之为“函数表达式”,会有提升机制,如果是如下的这种“函数声明”方式,则完全没有提升机制方面的问题:
(function() {
fun();
function fun() {
console.log("Hello!"); //"Hello!"
}
})();
这也是函数声明与函数表达式的主要区别。
三、函数传参变量提升问题(证明知识点四)
1、eg:var foo=1;
(function (foo) {
console.log(foo);//1
foo=3;
var foo=2;
console.log(foo);//2
})(foo);//1
console.log(foo);
解析:函数参数是原始类型的值(数值、字符串、布尔值),传递方式是传值传递,即在函数体内修改参数值,不会影响到函数外部。
2、eg:var foo={n:1};
(function (foo) {
console.log(foo.n);//1
foo.n=3;
var foo={n:2};
console.log(foo.n);//2
})(foo);
console.log(foo.n);//3
解析:函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递,传入的是原始值的地址,因此在函数内部修改参数,将会影响到原始值。
3、eg:var foo={n:1};
(function (foo) {
console.log(foo.n);//1
foo = {n:3};
var foo={n:2};
console.log(foo.n);//2
})(foo);
console.log(foo.n);//1
解析:如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值!
4、eg:var foo = {n:1};
(function(foo){ //形参foo同实参foo一样指向同一片内存空间,这个空间里的n的值为1
var foo; //优先级低于形参,无效。
console.log(foo);
console.log(foo.n); //输出1
foo.n = 3; //形参与实参foo指向的内存空间里的n的值被改为3
foo = {n:2}; //形参foo指向了新的内存空间,里面n的值为2.
console.log(foo.n); //输出新的内存空间的n的值
})(foo);
console.log(foo.n); //实参foo的指向还是原来的内存空间,里面的n的值为3.
答案:{n:1} ,1,2,3