函数声明和函数表达式的区别
- 函数声明
function sayHello () {
console.log('hello')
}
sayHello ()
声明不必放在调用的前面
- 函数表达式
var sayHello = function () {
console.log('hello');
}
sayHello ()
声明必须放到调用的前面
变量与函数的声明前置
- 变量声明前置:JavaScript引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到当前作用域的开始部分。
- 函数声明前置:JavaScript引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,都会被提升到当前作用域的开始部分。
arguments
arguments对象是所有函数中可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数的条目,第一个条目的索引从0开始。
arguments对象不是一个 Array 。它类似于数组,但除了 长度之外没有任何数组属性。
function printPersonInfo(name, age, sex){
console.log(name); // "lily"
console.log(age); // 23
console.log(sex); // "female"
console.log(arguments); // [object Arguments] { 0: "lily", 1: 23, 2: "female" }
console.log(arguments[0]); // "lily"
}
printPersonInfo("lily",23,"female");
函数的"重载"如何实现
在JS中没有重载,同名函数会覆盖。但可以在函数中针对不同的参数调用执行相应的逻辑。
function printPeopleInfo(name,age,sex) {
if (name) {
console.log(name);
}
if (age) {
console.log(age);
}
if (sex) {
console.log(sex);
}
}
立即执行函数表达式
- 在Javascript中,一对圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。
- JavaScript引擎规定,如果function关键字出现在行首,一律解释成语句。JavaScript引擎看到行首是function关键字之后,认为这一段都是函数的定义,以圆括号结尾,会报错。
- 不让function出现在行首,让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。
- 这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称IIFE。
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
- 通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。
- 一是不必为函数命名,避免了污染全局变量;
- 二是IIFE内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
用递归来求n!
function result(n){
if (n<0) {
console.log("请输入大于等于0的整数");
}
else if (n===0||n===1) {
return 1;
}
else return n*result(n-1)
}
result(n)
以下代码输出什么?
getInfo('饥人谷', 2, '男');的输出结果为:
name: 饥人谷
age:2
sex:男
[“饥人谷”,2,“男”]
name valley
getInfo('小谷', 3);的输出结果为:
name:小谷
age:3
sex:undefined
[“小谷”,2]
name vally
getInfo('男');输出结果为:
name: 男
age:undefined
sex:undefined
[“男”]
name valley
写一个函数,返回参数的平方和
function sumOfSquares(){
var result=0;
for(var i=0; i<arguments.length; i++){
result+=arguments[i]*arguments[i];
}
return result;
}
var result=sumOfSquares(2,3,4);
var result2=sumOfSquares(1,3);
console.log(result); //29
console.log(result2); //10
代码分析1
输出为:
![Uploading 5_129092.png . . .]
undefined
报错
原因分析:
var a; //声明前置
console.log(a); //a已声明,但没有赋值,故为undefined
a = 1; //赋值a=1
console.log(b); //因为b没有被声明,故报错
代码分析2
输出为:
hello world
报错
原因分析:
function sayName(name){
console.log('hello ', name);
} //此为函数声明,声明不必放在调用的前面,故可以正常调用
var sayAge = function(age){
console.log(age);
} //此为函数表达式,声明必须放在调用的前面,而该代码将其放在了后面,故报错
如下代码输出什么? 写出作用域链查找过程伪代码
输出结果:10
作用域链:
globalContext = {
AO:{
x:10
foo:function
bar:function
}
Scope:null
}
foo:[[scope]]=globalContext.AO
bar:[[scope]]=globalContext.AO
barContext = {
AO:{
x:30
}
Scope:bar.[[scope]]
}
fooContext = {
AO:{}
Scope:foo.[[scope]]
}
如下代码输出什么? 写出作用域链查找过程伪代码
输出结果:30
作用域链:
globalContext = {
AO:{
x:10
bar:function
}
Scope:null
}
bar.[[scope]]=globalContext.AO
barContext = {
AO:{
x:30
foo:function
}
Scope:bar.[[scope]]
}
foo.[[scope]]=barContext
fooContext = {
AO:{}
Scope:foo.[[scope]]
}
以下代码输出什么? 写出作用域链的查找过程伪代码
输出结果:30
作用域链:
globalContext = {
AO:{
x:10
bar:function
}
Scope:null
}
bar.[[scope]]=globalContext.AO
barContext = {
AO:{
x:30
匿名函数:function
}
Scope:bar.[[scope]]
}
匿名函数.[[scope]]=barContext
//设匿名函数名称为someone
someoneContext = {
AO:{}
Scope:匿名函数.[[scope]]
}
以下代码输出什么? 写出作用域链查找过程伪代码
输出结果:
undefined
5
1
6
20
200
作用域链:
globalContext = {
AO:{
a:1
fn:function
fn3:function
}
Scope:null
}
fn.[[scope]]=globalContext.AO
fn3.[[scope]]=globalContext.AO
fnContext = {
AO:{
a:5
fn2:function
}
Scope:fn.[[scope]]
}
fn2.[[scope]]=fnContext
fn3Context = {
AO:{}
Scope:fn3.[[scope]]
}
fn2Context = {
AO:{}
Scope:fn2.[[scope]]
}
简单解析:
先声明前置
然后调用函数,首先调用fn();
fn()有其自己的AO
同样先声明前置
第一个console.log时没有给a赋值,但已声明,故为undefined //第一个结果
然后给a赋值为5
则第二个console.log为5 //第二个结果
a自加,此时fnContext的AO中的a为6;
调用函数fn3
其AO为空,调用其scope,使用globalContext的AO
故console.log为1 //第三个结果
给a赋值200
则此时globalContext的AO中的a赋值为200;
调用函数fn2
其AO为空,调用其scope,使用fnContext的AO,为6 //第四个结果
给a赋值20
则此时fnContext的AO中的a赋值为20; //第五个结果
最后,console.log(a),输出globalContext的a值,为200 //第六个结果