5.函数(下)
1.作用域
在 JavaScript 中, 作用域为可访问变量(包含对象和函数)的集合。
通俗讲,作用域就是起作用的范围。
在 JavaScrip 中,有全局作用域和局部作用域。
全局作用域
整个页面起作用,在<script>内都能访问到;
在全局作用域中有全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接调用;
全局作用域中声明的变量和函数,会作为window对象的属性和方法保存;
变量在函数外声明,即为全局变量,拥有全局作用域。
<script>
var a = 123;//全局变量
function fn() {
console.log(a);//123
}
fn();
console.log(a);//123
</script>
在 JavaScrip 中,函数是唯一拥有自身作用域的代码块。
局部作用域
局部作用域内的变量只能在函数内部使用,所以也叫函数作用域;
变量在函数内声明,即为局部变量,拥有局部作用域。
<script>
function fn() {
var b = 456;//局部变量
console.log(b);//456
}
fn();
console.log(b);//b is not defined
</script>
可以直接给一个未声明的变量赋值(全局变量),但不能直接使用未声明的变量!
因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。
变量生命周期
全局变量在页面打开时创建,在页面关闭后销毁。
局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。
2.声明提升机制
在 JavaScrip 中变量声明和函数声明,声明会被提升到当前作用域的顶部。
var a = 1;
function test() {
alert(a);
var a = 2;
alert(a);
}
test();
在同一作用域中:
alert(typeof fn);
var fn = 10;
function fn() {}
alert(typeof fn);
JS解析器
浏览器中有一套专门解析JS代码的程序,将这个程序称为js的解析器。
浏览器运行整个页面文档时,遇到<script>标签时JS解析器开始解析JS代码。
JS解析器的工作步骤:
1.预解析代码
主要找一些关键字如var、function 、参数等,并存储进仓库里面(内存);
变量的初始值为 undefined;
函数的初始值就是该函数的代码块;
当变量和函数重名时:不管顺序谁前谁后,只留下函数的值;
当函数和函数重名时:会留下后面那个函数。
2.逐行执行代码
当预解析完成之后,就开始逐行执行代码,仓库中变量的值随时都可能会发生变化
3.自执行函数
要执行一个函数,我们必须要有方法定位函数、引用函数。
匿名函数如何调用?
自执行函数即声明完了马上进行调用(IIFE立即执行函数)。
(function () {
console.log(123);
})();
小括号能把我们的表达式组合分块,并且每一块都有一个返回值,这个返回值实际上就是小括号中表达式的返回值。
自执行函数的好处:独立的作用域,不会污染全局环境!
传参:
(function (a,b) {
console.log(a + b);
})(2,3);
常见形式:
(function () {
console.log(11);
})();
(function(){
console.log(22);
}());
!function() {
console.log(33);
}();
+function() {
console.log(55);
}();
-function() {
console.log(66);
}();
......
4.递归函数
老王有四个子女,老四比老三小2岁,老三比老二小2岁,老二比老大小2岁,老大现在16岁,问老四几岁?
公园里有一堆桃子,猴子每天吃掉一半,挑出一个坏的扔掉,第6天的时候发现还剩1个桃子,问原来有多少个桃子?
阿里巴巴2015年前端面试题
/**
*@desc: fibonacci
*@param: count {Number}
*@return: result {Number} 第count个fibonacci值,计数从0开始
fibonacci数列为:[1, 1, 2, 3, 5, 8, 13, 21, 34 …]
getNthFibonacci(0) 返回值为1
getNthFibonacci(4) 返回值为5
*/
function f1( ){
// ...
}
f1( ); // 函数调用
function f2( ){
f1( ); // 函数调用
}
如果一个函数直接或间接调用函数本身,这个函数就是递归函数。
递归函数的本质:函数循环调用。
老王有四个子女,老四比老三小2岁,老三比老二小2岁,老二比老大小2岁,老大现在16岁,问老四几岁?
function countAge(who) {
if (who == 1) {
return 16;
} else {
return countAge(who - 1) - 2;
}
}
alert(countAge(4)); // 10
递归函数在运行的时候,每调用一次函数就会在内存中开辟一块空间,内存消耗较大,注意防止栈溢出。
一般来说,递归需要有边界条件、递归前进段和递归返回段。
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
公园里有一堆桃子,猴子每天吃掉一半,挑出一个坏的扔掉,第6天的时候发现还剩1个桃子,问原来有多少个桃子?
var x;
function shuliang(n) {
if (n == 6) {
x = 1;
}else {
x = (shuliang(n + 1) + 1) * 2;
}
return x;
}
alert(shuliang(0)); // 190
阿里巴巴2015年前端面试题
/**
*@desc: fibonacci
*@param: count {Number}
*@return: result {Number} 第count个fibonacci值,计数从0开始
fibonacci数列为:[1, 1, 2, 3, 5, 8, 13, 21, 34 …]
getNthFibonacci(0) 返回值为1
getNthFibonacci(4) 返回值为5
*/
斐波那契数列:[1, 1, 2, 3, 5, 8, 13, 21, 34...] 数列从第3项开始,每一项都等于前两项之和
斐波那契数列的定义者,是意大利数学家列昂纳多·斐波那契
function getNthFibonacci(count) {
var count = parseInt(count);
if (isNaN(count) || count < 0) {
return 0;
}
function fn(count) {
if (count <= 1) {
return 1;
}
return fn(count - 1) + fn(count - 2);
}
return fn(count);
}
console.log( getNthFibonacci(0) ); // 1
console.log( getNthFibonacci(4) ); // 5
递归算法一般用于解决三类问题:
1.数据的定义是按递归定义的;
2.问题解法按递归算法实现;
3.数据的结构形式是按递归定义的。
5.构造函数及对象类型(了解)
构造函数:用于创建特定类型的对象。
JS内部构造函数:Object、Number、String、Array、Function、Boolean等等...
当任意一个普通函数用于创建一类对象,并通过new操作符来调用时它就可以作为构造函数。
构造函数一般首字母大写。