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操作符来调用时它就可以作为构造函数。
 
构造函数一般首字母大写。
posted @ 2019-06-25 18:56  前端小菜鸟吖  阅读(235)  评论(0编辑  收藏  举报