1.函数声明和函数表达式的区别?
- 在ECMAScript中,创建函数的最常用的两个方法是函数表达式和函数声明,两者期间的区别是有点晕,因为ECMA规范只明确了一点:函数声明必须带有标示(Identifier)(就是大家常说的函数名称),而函数表达式则可以省略这个标示符:
- 函数声明:
function 函数名称(参数:可选){函数体} - 函数表达式:
function 函数名称(可选)(参数:可选){函数体} - 小结:
- 若不声明函数的名称,它就是函数表达式
- 若同时声明了函数名称,ECMAScript是通过上下文来区分的,如果function foo(){}是作为赋值表达式的一部分的话,例如:
var fun1=function foo(){}
,那它就是一个函数表达式。如果function foo(){}被包含在一个函数体内,或者位于程序的最顶部的话,那它就是一个函数声明。 - 特殊的函数表达式,如:
(function foo(){})
它是表达式的原因是因为括号 ()是一个分组操作符,它的内部只能包含表达式,同理如,JSON字符串通常被包含在一个圆括号里:eval('(' + json + ')'),这样做的原因就是因为分组操作符,也就是这对括号,会让解析器强制将JSON的花括号解析成表达式。
2.什么是变量的声明前置?什么是函数的声明前置?
-
所谓的变量声明前置就是在一个作用域块中,所有的变量都被放在块的开始出声明,下面举个例子你就能明白了
1.变量a 在函数外面
var a=1;
function fun1(){
console.log(a);// 1
}
main();//输出 1var a = 1; function fun1(){ console.log(a); var a = 2; } main()//输出undefined
2.同样是变量a在函数外层,为什么出现undefined,这是因为JS在执行时候,会自动将变量声明前置,解析步骤如下:
var a=1;
function fun1(){
var a;// a 此时为undefined
console.log(a);//输出结果为undefined
a=2;//a 被赋值为2,但没有输出
}
fun1();//undefined
- 和变量的声明会前置一样,函数声明同样会前置.
1.如果我们使用函数表达式那么规则和变量一样;
console.log(fun1) //undefined
var fun1= function(){}
2.我们使用函数声明的方式,那么即使函数写在最后也可以在前面语句调用,前提是函数声明部分已经被下载到本地
fun1();//结果为1
function fun1(){
console.log(1); //1
}
3.arguments 是什么?
- arguments 是是JavaScript里的一个内置对象,它很古怪,也经常被人所忽视,但实际上是很重要的。所有主要的js函数库都利用了arguments对象。所以agruments对象对于javascript程序员来说是必需熟悉的。所有的函数都有属于自己的一个arguments对象,它包括了函所要调用的参数。他不是一个数组,如果用typeof arguments,返回的是'object'。虽然我们可以用调用数据的方法来调用arguments。比如length,还有index方法。但是数 组的push和pop对象是不适用的
- 在函数内部,你可以使用arguments对象获取到该函数的所有传入参数
function info(name,sex,age){
console.log(name);//"小明"
console.log(sex);//"男"
console.log(age);//18
console.log(arguments[0]);//"小明"
console.log(arguments[1]);//"男"
console.log(arguments[2]);//18
console.log(arguments);//"小明","男",18
console.log(arguments.length)//3
}
info("小明","男",18);//
info("小明","男");// “小明”,“男”,undefined 2
浏览器输出结果:
![3BXV%DI)S@R8EB2@$NL26F.png
4. 函数的重载怎样实现
- 重载是很多面向对象语言实现多态的手段之一,在静态语言中确定一个函数的手段是靠方法签名——函数名+参数列表,也就是说相同名字的函数参数个数不同或者顺序不同都被认为是不同的函数,称为函数重载
- 在JavaScript中没有函数重载的概念,函数通过名字确定唯一性,参数不同也被认为是相同的函数,后面的覆盖前面的
- 在js中,我们实现重载常用的方式有:
1、根据传入参数的类型执行不同的操作。
2、利用参数中特殊的参数值进行不同的操作。
3、根据参数的个数进行重载。
这里对第3种实现方式进行说明
function f(length) {
var len= arguments.length;
if(1 == len) {
var width = arguments[1];
alert("高为:"+length+",宽为:"+width);
} else {
alert("高为:"+length);
}
}
f(10);// 高为10
f(10,10);高为10,宽为10
你就可以给函数f()传入一个参数也可以传入两个参数了
5. 立即执行函数表达式是什么?有什么作用
- 立即执行函数就是声明一个匿名函数,马上调用这个匿名函数
- 立即执行函数表达式有两种写法
- 第一种
(function(){ }()); - 第二种
(function(){ })();
- 只有一个作用:创建一个独立的作用域。这个作用域里面的变量,外面访问不到(即避免「变量污染」),例如:
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
liList[i].onclick = function(){
alert(i) // 为什么 alert 出来的总是 6,而不是0、
1、2、4、5
}
}
这是因为i贯穿了整个作用域,而在js中除了函数内有作用域,{ }里的内容不是作用域,因而for运行完后i的值为6,而用户也是在for运行完才点击,此时i为6。
解决整个问题就要立即执行函给每个li创造独立的作用域
var liList = ul.getElementsByTagName('li')
for(var i=0; i<6; i++){
!function(li){
liList[li].onclick = function(){
alert(li) // 0、1、2、3、4、5
}
}(i)
}
在立即执行函数执行的时候,i 的值被赋值给 ii,此后 ii 的值一直不变。i 的值从 0 变化到 5,对应 6 个立即执行函数,这 6 个立即执行函数里面的 ii 「分别」是 0、1、2、3、4、5。
6. 什么是函数的作用域链
- 作用域链是存储变量对象的集合(环境栈),保证对执行环境有权访问的所有变量和函数的有序访问,也就是用于标识符解析(变量访问)。
- 在javascript没有块级作用域,是由函数来划分的。变量和函数的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域(with和eval除外)。当定义了一个函数,当前的作用域链就保存起来,并且成为函数的内部状态的一部份。在最顶级作用域链仅由全局对象组成,而不和词法作用域相关,然而,当定义一个嵌套的函数时,作用域链就包括外面的包含函数。这意味着嵌套函数可以访问包含函数的所有参数和局部变量。尽管当一个函数定义时作用域链就固定了,但作用域链中定义的属性还没有固定。作用域链是活的,并且函数被调用时,可以访问任何当前的绑定。
var a;//全局作用域
function b(){
var c ;//c位于函数b的作用域
function d(){
var e; //e位于函数d的作用域
alert(a);
}
} - 当alert(a)时,jsJS引擎沿着d的作用域, b的作用域, 全局作用域的顺序进行查找,这三个作用域组成的有序集合就成为作用域链