2021/10/15 前端开发之JavaScript+jQuery入门 第二章JavaScript函数

目录

1.函数简介

2.系统函数

3.函数表达式

4.自定义函数的定义

5.函数调用

5.1作为函数调用

5.2作为方法调用

5.3作为构造函数调用

5.4间接调用

5.5通过事件调用

6.事件

7.函数的实参和形参 

5.1实参对象(arguments)

5.2callee和caller属性

5.3将对象属性用做实参

5.4实参类型校验

8.作为值的函数

9.自定义函数的属性

10.作为命名空间的函数

11.闭包

12.函数属性、方法和构造函数

        12.1 length属性

        12.2 prototype属性

        12.3 call()和apply()方法

        12.4 bind()方法

        12.5 toString()方法

        12.6 Function()构造函数

13.可调用对象(callable object)

14.函数式编程

        14.1 使用函数处理数组

        14.2 高阶函数(higher-order function)

        14.3 不完全函数(partial function)

        14.4 记忆(memorization)

15.匿名函数及自调用

16.回调函数

17.预解析

18.作用域和作用域链

本章总结


1.函数简介

        在JavaScript中,函数即对象,程序可以随意操控它们。比如,JavaScript可以把函数赋值给变量,或者作为参数传递给其他函数,并且可以给它们设置属性,甚至调用它们的方法。函数分系统函数与自定义函数,自定义函数是用的最多的类似于Java中的方法。

2.系统函数

        2.1 parselnt() 可以解析一个字符串返回一个整数

var num1=parselnt("56.64");  //返回值为56
var num2=parselnt("123abc"); //返回值为123
var num3=parselnt("abc123");  //返回值为NaN

        2.2parseFloat() 可解析一个字符串 返回一个浮点数

var num1=parseFloat("56.64");  //返回值为56.64
var num2=parseFloat("123abc"); //返回值为123
var num3=parseFloat("abc123");  //返回值为NaN
var num=parseFloat("52.18.97"); //返回52.18

        2.3isNaN() 函数用于检查其参数是否是非数字

var num1=isNaN("56.64");  //返回值为false
var num2=isNaN("123abc"); //返回值为true
var num3=isNaN("abc123");  //返回值为true

3.函数表达式

        JavaScript中,把一个函数赋值给一个变量,此时形成了函数表达式

var 变量 = function(参数列表){
    //JavaScript语句
};

4.自定义函数的定义

        一般函数定义的形式有以下几种:

  • 函数声明法
    function factorial(x) {
        if(x <= 1) return 1;
        return x * factorial(x - 1);
    }

    注意:函数声明语句"被提前"到脚本的顶部,所以可以在定义之前的代码调用函数。

  • 函数赋值法(可定义为匿名函数)
    var square = function(x) { return x*x; }

    注意:变量的声明是可以提前的,但是给变量的赋值不会提前,所以这种方式的函数在定义之前无法调用。

  • 函数定义后立即执行
    var tensquared = (function(x) { return x*x; }(10));

    注意: function左边的左括号是必需的,因为如果不写左括号,JavaScript解释器会将关键字function解析为函数声明语句。使用左括号后,JavaScript解释器才会正确地将其解析为函数定义表达式

  • 嵌套函数
    function hypotenuse(a, b) {
        function square(x) { return x*x; }
        return Math.sqrt( square(a) + square(b) );
    }

    注意:嵌套函数中,注意作用域的使用。

5.函数调用

有4种方式可以调用JavaScript函数:

  • 作为函数
  • 作为方法
  • 作为构造函数
  • 通过call()和apply()方法间接调用
  • 通过事件调用

5.1作为函数调用

函数调用就是直接通过function对象来调用函数。

var probability = factorial(5) / factorial(13);

根据ECMAScript3和非严格的ECMAScript5的规定,函数调用的上下文(this的值)是全局对象(window)。在ECMAScript 5的严格模式下,调用上下文是undefined。

5.2作为方法调用

  • 方法调用是通过对象的属性来调用函数。
  • 方法调用和函数调用最大的一个区别是:调用上下文。方法调用的上下文是调用它的对象,可以通过this关键字引用该对象。
  • this是一个关键字,不是变量,也不是属性名。JavaScript不允许给this赋值。
var calculator = {
    operand1: 1,
    operand2: 2,
    add: function() {
        this.result = this.operand1 + this.operand2;
    }
};

calculator.add();       // 方法调用
calculator["add"]();    // 另一种形式的方法调用
calculator.result;      // 2,对象属性添加成功
  • 如果嵌套函数作为函数调用,则this值不是全局对象就是undefined。
var o = {
  m: function() {
    var self = this;
    console.log(this === o);            // true
    f();                                // 作为函数调用
    
    function f() {
      console.log(this === o);          // false
      console.log(this === window);     // true
    }
  }
}
  • 如果嵌套函数作为方法调用,则this值指向调用它的对象。
var o = {};
function outer() {
    var inner = function() {
        console.log(this === o);
    };
    o.m = inner;
}
outer();
o.m();          // 输出: true,作为方法调用

5.3作为构造函数调用

  • 如果函数或方法调用之前带有关键字new,它就构成构造函数调用。
  • 构造函数调用和普通的函数调用以及方法调用在实参处理、调用上下文和返回值方面都有不同。
  • 构造函数调用会创建一个新的空对象,这个对象继承自构造函数的prototype属性。构造函数使用这个新创建的对象作为调用上下文,并可以使用this关键字引用这个新创建的对象。
    var o = {
        m: function() {
            console.log(this === o);
        }
    };
    
    
    o.m();              // true,方法调用
    var s = new o.m();  // false,构造函数调用
    

5.4间接调用

  • JavaScript中的函数也是对象,函数对象也可以包含方法。其中的2个方法call()和apply()可以用来间接地调用函数。
  • 两个方法允许显式指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。
  • 在ECMAScript3和非严格模式中,传入的null和undefined都会被全局对象代替,而其他原始值则会被相应的包装对象所替代。
f.call(o);
f.apply(o);

5.5通过事件调用

  • 可以通过事件调用多个方法
function aaa (book) {
	var node=document.getElementById(book);
	node.className="c";
}
function bbb (book) {
	var node=document.getElementById(book);
	node.className="b";
}
bbb("book2");
bbb("book3");
<ul><!-- 在 js/tab.js 中定义方法切换显示 -->
		<li id="bg1"><a class="white" onmouseover="aaa('book1'),bbb('book2'),bbb('book3')">小说</a></li>
		<li id="bg2"><a class="white" onmouseover="aaa('book2'),bbb('book1'),bbb('book3')">非小说</a></li>
		<li id="bg3"><a class="white" onmouseover="aaa('book3'),bbb('book1'),bbb('book2')">少儿</a></li>
	</ul>

6.事件

  1. onload:一个页面或一副图像完成加载
  2. onclick:鼠标单击某个对象
  3. onmouseover: 鼠标指针移到某个元素上
  4. onmouseout: 鼠标指针离开某个元素
  5. onkeydown:某个键盘按键被按下
  6. onchange:域的内容被改变

7.函数的实参和形参 

JavaScript中的函数定义并未指定函数形参的类型,函数调用也未对传入的实参值做任何类型检查。实际上,JavaScript函数调用甚至不检查传入形参的个数。函数对于未传入的参数赋值为undefined,对于多余的参数忽略。

###可选形参
由于调用函数传入的实参个数可以比形参个数少,所以函数应当对此有一个较好的适应性,给省略的参数赋一个合理的默认值。

function getPropertyNames(o, /* optional */ a) {
    if (a === undefined)  a = [];    // 如果未定义,则使用新数组
    
    for (var property in o) 
        a.push(property);
        
    return a;
}

var a = getPropertyNames(o);    // 将o的属性存储到一个新数组中
getPropertyNames(p, a);         // 将p的属性追加到数组a中

其中第一个if判断语句可简写为:

a = a || [];

5.1实参对象(arguments)

  • 实参对象是一个类数组对象,可以通过数字下标访问传入函数的实参值,而不用非要通过形参名字来得到实参。

下面的函数可以接收任意数量的实参:

function max(/* ... */) {
    var max = Number.NEGATIVE_INFINITY;
    // 遍历实参,查找并记住最大值
    for(var i = 0; i < arguments.length; i++) 
        if(arguments[i] > max) max = arguments[i];
    
    return max;
}
  • 在非严格模式下,实参对象的数组元素是函数形参所对应实参的别名,可以通过实参对象修改实参的值。
  • 在非严格模式下,arguments仅仅是一个标识符,在严格模式中,它变成了一个保留字。严格模式中的函数无法使用arguments作为形参名或局部变量名,也不能给arguments赋值。
function f(x) {
    console.log(x);         // 输出实参的初始值
    arguments[0] = null;    // 修改实参数组元素同样会修改x的值
    console.log(x);         // 输出"null"
}

5.2callee和caller属性

  • 除了数组元素,实参对象还定义了callee和caller属性。
  • callee属性指代当前正在执行的函数。caller指代调用当前正在执行的函数的函数。

在匿名函数中,可通过callee来递归地调用自身:

var factorial = function(x) {
    if (x <= 1) return 1;
    return x * arguments.callee(x-1);
}

5.3将对象属性用做实参

当一个函数包含超过3个形参时,对于程序员说,要记住调用函数中实参的正确顺序比较困难。最好通过名/值对的形式来传入参数,这样参数的顺序就无关紧要了。

function arraycopy(/* array */ from, /* array */ to, /* integer */ length) {
    // 逻辑代码
}

// 使用对象当参数
function easycopy(args) {
    arraycopy(args.from, args.to, args.length);
}

var a = [1,2,3,4], b = [];
easycopy({ from: a, to: b, length: 4});

5.4实参类型校验

JavaScript会在必要的时候进行类型转换,如果期望的实参是一个字符串,那么实参值无论是原始值还是对象都可以很容易地转换成字符串。但是当期望的实参是一个数组时,就无法对非数组对象进行转换了,所以有必要对实参进行校验。

function flexisum(a) {
    var total = 0;
    for (var i=0; i < arguments.length; i++) {
        var element = arguments[i], n;
        
        if (element == null) continue;          // 忽略null和undefined实参
        if (isArray(element))
            n = flexisum.apply(this, element);  // 如果是数组,递归计算累加和
        else if (typeof element === "function")
            n = Number(element());              // 如果是函数,调用它并做类型转换
        else
            n = Number(element);
        
        if (isNaN(n))
            throw Error("flexisum(): can't convert" + element + " to number.");
        
        total += n;
    }
    
    return total;
}

8.作为值的函数

在JavaScript中,函数不仅是一种语法,也是值,也就是说,可以将函数赋值给变量,存储在对象的属性或数组的元素中,作为参数传入另外一个函数等。

// 声明一个函数
function square(x) { return x*x; }

var s = square;         // 赋值给变量
s(4);                   // => 16

var o = { m: square };  // 赋值给对象
o.m(5);                 // => 25

var a = [square, 6];    // 赋值给数组
a[0](a[1]);             // => 36

9.自定义函数的属性

函数是一种特殊的对象,所以可以为函数定义属性以完成特殊的需求。
可以通过函数属性实现"静态"变量的需求。

// 由于函数声明被提前了,因此可以在声明之前赋值
uniqueInteger.counter = 0;

function uniqueInteger() {
    return uniqueInteger.counter++;
}

var a = uniqueInteger();    // 0
a = uniqueInteger();        // 1
a = uniqueInteger();        // 2

10.作为命名空间的函数

不在任何函数内声明的变量是全局变量,在整个JavaScript程序中都是可见的。基于这个原因,我们常常简单地定义一个函数用做临时的命名空间,在这个命名空间内定义的变量都不会污染到全局命名空间。

function mymodule() {
    // 这个模块所使用的所有变量都是局部变量
    // 而不会污染全局命名空间
}

11.闭包

JavaScript也采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是调用时决定的。为了实现这种词法作用域,JavaScript函数对象的内部状态不仅包含函数的代码逻辑,还包含函数定义时的作用域链。
函数体内部的变量都可以保存在函数作用域内,看起来是函数将变量"包裹"起来了,这种特性称为"闭包"。

var scope = "global scope";
function checkscope() {
    var scope = "local scope";
    
    // 定义时使用局部变量
    function f() { return scope; }
    
    return f(); // 返回函数的调用结果
}

checkscope();   // => "local scope"

如果更改下checkscope定义,将返回值更改为函数定义,如下:

var scope = "global scope";
function checkscope() {
    var scope = "local scope";
    
    // 定义时使用局部变量
    function f() { return scope; }
    
    return f;   // 返回函数的定义
}

checkscope()();   // => "local scope"

虽然调用函数的作用域变了,但是函数的输出结果依然不变,因为函数保存了自己的作用域链。
通过闭包,可以实现一个更好的计数器类,将变量包裹起来:

function counter() {
    var n = 0;
    return {
        count: function() { return n++; }
        reset: function() { n = 0; }
    };
}

var c = counter();
c.count();          // => 0
c.count();          // => 1
c.reset();          // => 0

12.函数属性、方法和构造函数

        12.1 length属性

arguments.length表示传入函数的实参的个数。函数的length属性表示函数形参的个数,这个属性是只读的。
可以通过这个属性对函数的参数个数进行校验:

function check(args) {
    var actual   = args.length;
    var expected = args.callee.length;  // 形参个数
    if (actual != expected) {
        throw Error("Expected " + expected + "args; got " + actual);
    }
}

        12.2 prototype属性

这个属性指向一个对象的引用,这个对象称做"原型对象"。每一个函数都包含不同的原型对象。当将函数用做构造函数的时候,新创建的对象会从原型对象上继承属性。

        12.3 call()和apply()方法

这2个方法属于函数的方法属性,在前面已经有介绍,不再重复介绍。

        12.4 bind()方法

bind()是在ECMAScript5中新增的方法,这个方法的主要作用就是返回一个新的函数,这个函数将bind的对象作为调用上下文

function f(y) { return this.x + y; }
var o = { x: 1 };
var g = f.bind(o);  // bind返回一个函数
g(2);               // =>3,以对象o作为调用上下文执行f(y)

可以通过如下代码来实现简单的bind():

function bind(f, o) {
    if (f.bind) return f.bind(o);
    else return function() {
        // 此处的arguments为调用bind返回函数时传递的参数
        // 上例中为2(g(2))
        return f.apply(o, arguments);   
    }
}

但ECMAScript5中的bind()方法不仅仅是将函数绑定至一个对象,它还能将实参也绑定至this,这种编程技术, 有时被称为"柯里化"(currying)。参照下面的例子:

function f(y,z) { return this.x + y + z; };
var g = f.bind({x: 1}, 2);  // 绑定this和y
g(3);                       // =>6,this.x绑定到1,y绑定到2,z绑定到3

下面的代码给出了更标准的bind()方法,将这个方法另存为Function.prototye.bind:

if( !Function.prototye.bind) {
    Function.prototye.bind = function(o /*, args */) {
        // 将this和arguments的值保存在变量中
        // 以便在后面嵌套的函数中使用
        var self = this, boundArgs = arguments;
        
        // bind()返回一个函数
        return function() {
            // 创建一个实参列表,保存传入的所有实参
            var args = [], i;
            for(i = 1; i < boundArgs.length; i++) args.push(boundArgs[i]);
            for(i = 0; i < arguments.length; i++) args.push(arguments[i]);
            
            // 以绑定对象o作为上下文来调用函数self
            // 并传递所有的实参args
            return self.apply(o, args);
        };
    };
}

ECMAScript5定义的bind()方法有一些特性是上述代码无法模拟的:

  • 真正的bind()方法返回的函数,length属性是绑定函数的形参个数减去绑定的实参个数。
  • 真正的bind()方法可以创建构造函数,如果bind()返回的函数用做构造函数,将忽略bind()传入的this,但是实参会正常绑定。
  • 由bind()方法返回的函数并不包含prototype属性,如果返回函数用做构造函数,则创建的对象从原始的未绑定构造函数中继承prototype,同时,使用instanceof运算符,绑定构造函数和未绑定构造函数并无两样。
function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() { 
  return this.x + ',' + this.y; 
};

var p = new Point(1, 2);
p.toString(); // '1,2'

var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);
// 以下这行代码在 polyfill 不支持,
// 在原生的bind方法运行没问题:
//(译注:polyfill的bind方法如果加上把bind的第一个参数,即新绑定的this执行Object()来包装为对象,Object(null)则是{},那么也可以支持)
// var YAxisPoint = Point.bind(null, 0/*x*/);

// 绑定函数的length属性 = 形参个数 - 绑定实参个数
console.log(YAxisPoint.length);                         // =>1,(2-1)

// 绑定函数不包含prototype属性
console.log(Point.prototype);                           // "Point { toString-function()}"
console.log(YAxisPoint.prototype);                      // undefined

// 绑定函数用做构造函数
// this指代新创建的对象
var axisPointA = new YAxisPoint();
console.log(axisPointA.toString());                     // '0,undefined'

var axisPoint = new YAxisPoint(5);
console.log(axisPoint.toString());                      // '0,5'

// 使用instanceof时,绑定构造函数和未绑定构造函数并无两样
console.log(axisPoint instanceof Point);                // true
console.log(axisPoint instanceof YAxisPoint);           // true
console.log(new Point(17, 42) instanceof YAxisPoint);   // true

        12.5 toString()方法

和所有的JavaScript对象一样,函数也有toString()方法。实际上,大多数(非全部)的toString()方法的实现都返回函数的完整源码。内置函数往往返回一个类似"[native code]"的字符串。

        12.6 Function()构造函数

前面已经介绍,函数可以通过定义语句或直接量表达式来定义。函数还可以通过Function()构造函数来定义。
Function()构造函数可以传入任意数量的实参,最后一个实参表示的是函数体。
Function()构造函数并不需要通过传入实参以指定函数名。Function()会构造一个匿名函数。

var f = new Function("x", "y", "return x*y;");

// 这个定义与下面的函数定义等价
var f = function(x, y) { return x*y; }

Function()构造函数有以下几个特点:

  • Function()在运行时动态地创建并编译函数。
  • 每次调用Function()构造函数都会解析函数体,并创建新的函数对象。
  • Function()构造函数创建的函数并不使用词法作用域,函数体代码的编译总是在顶层函数执行
var scope = "global";
function constructFunction() {
    var scope = "local";
    
    return new Function("return scope");    // 无法捕获局部作用域
}

constructFunction()();                      // => "global"

13.可调用对象(callable object)

可调用对象是一个对象,可以在函数调用表达式中调用这个对象。所有的函数都是可调用的,但并非所有的可调用对象都是函数。

  • IE8之前的版本实现了客户端方法(诸如window.alert()和Document.getElementsById()),使用了可调用的宿主对象,而不是内置函数对象。IE9将它们实现为真正的函数,因此此类可调用的对象越来越罕见。
  • 另外一个常见的可调用对象是RegExp对象,但代码最好不要对可调用的RegExp对象有太多依赖,对RegExp执行typeof运算的结果并不统一,在有些浏览器中返回"function",在有些返回"object"。

检测一个对象是否是真正的函数对象:

function isFunction() {
    return Object.prototye.toString.call(x) === "[object Function]";
}

14.函数式编程

和Lisp、Haskell不同,JavaScript并非函数式编程语言,但可以像操控对象一样操控函数,也就是说,JavaScript中可以应用函数式编程技术

        14.1 使用函数处理数组

使用函数式编程,简洁地实现计算平均值、标准差:

// 首先定义2个函数对象
var sum    = function(x,y) { return x+y; }
var square = function(x)   { return x*x; }

// 使用函数式编程计算平均数、标准差
var data = [1,1,3,5,5];
// 计算平均数
var mean = data.reduce(sum) / data.length;  

// 计算标准差
var deviations = data.map(function(x) { return x-mean; });
var stddev     = Math.sqrt(deviations.map(square).reduce(sum) / (data.length-1));

        14.2 高阶函数(higher-order function)

高阶函数就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数。

function mapper(f) {
    return function(a) { return a.map(f); } // 注意: 此处没有对参数a进行数组验证
}

var increment   = function(x) { return x+1; }
var incrementer = mapper(increment);
incrementer([1,2,3]);   // => [2,3,4]

        14.3 不完全函数(partial function)

不完全函数是一种函数变换技巧,即把一次完整的函数调用拆成多次函数调用,每次传入的实参都是完整实参的一部分,每个拆分开的函数叫做不完全函数,每次函数调用叫做不完全调用(partial application)。

// 实现一个工具函数,将类数组对象转换为真正的数组
function array(a, n) { return Array.prototye.slice.call(a, n || 0); }

// 将第1次调用的实参放在左侧
function partialLeft(f /* , ... */ ) {
    var args = arguments;
    return function() {
        var a = array(args, 1);          // 获取第1个参数之后所有的实参
        a = a.concat(array(arguments));
        return f.apply(this, a);
    };
}

// 将第1次调用的实参放在右侧
function partialRight(f /* , ... */ ) {
    var args = arguments;
    return function() {
        var a = array(arguments);
        a = a.concat(array(args, 1));
        return f.apply(this, a);
    };
}


// 将第1次调用实参中的undefined值替换成第2次调用的实参
function partial(f /* , ... */ ) {
    var args = arguments;
    return function() {
        var a = array(args, 1);
        var i = 0, j = 0;
        for(; i < a.length; i++) {
            if(a[i] === undefined) a[i] = arguments[j++];
        }
        a = a.concat(array(arguments, j));
        return f.apply(this, a);
    };
}

// 这个函数带有3个参数
var f = function(x, y, z) { return x * (y - z); };

partialLeft(f, 2)(3, 4);        // => -2  [2 * (3 - 4)]
partialRight(f, 2)(3, 4);       // => 6;  [3 * (4 -2)]
partial(f, undefined)(3, 4);    // => -6; [3 * (2 - 4)]

        14.4 记忆(memorization)

记忆只是一种编程技巧,本质上是以空间换时间,在客户端代码中,执行时间往往成为瓶颈,因此这种做法是非常可取的。
下面定义一个高阶函数,接收一个函数作为实参,并返回带有记忆能力的函数:

function memorize(f) {
    var cache = {};
    
    return function() {
        var key = arguments.length + Array.prototye.join.call(arguments, ",");
        if(key in cache) 
            return cache[key];
        else
            return cache[key] = f.apply(this, arguments);
    };
}

// 定义有记忆功能的斐波那契函数
var factorial = memorize(function(n) {
                            return (n <= 1) ? 1: n * factorial(n-1);
                        });
factorial(5);   // => 120,同时记忆了4~1的值

15.匿名函数及自调用

匿名函数只能调用一次,可以用来初始化操作,也能保证一些安全性

(function (conut){
        for (var i = 0; i < conut; i++) {
            document.write("欢迎学习javascript")
        })();

16.回调函数

将方法当作参数带入方法中

function f1(fn){
    console.log("f1函数调用了")
    fn();
    
}
function f2(){
   console.log("f2函数调用了")
}
f1(f2);

当掉用f1函数时,f2函数将作为参数传入,运行时首先输出"f1函数调用了",之后输出"f2函数调用了"

f2()函数作为参数传递到f1中,并且在f1函数中执行fn()函数,这时,f2()函数可以被称作为调回函数,如果没有指定函数名,可以被称作匿名回调函数

17.预解析

    JavaScript中,两个函数名一样时,前一个会被后一个覆盖
    但是如果将函数用变量来接收时,就不会被覆盖
    因为js的函数存在预解析
    
    变量的声明会提前,但是赋值不会预解析,就是说使用变量在定义变量之前的话,会出现 nudefined 也就是未赋值
    函数的声明也会提前

18.作用域和作用域链

18.1 作用域( Scope )就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。

块级作用域是由大括号限定的,在块级作用域下,所有的变量都定义在大括号内的,从定义开始到大括号结束这个范围可以使用

// var 可写可不写,如果不写就会变为隐式全局变量
//只有在方法中定义的 变量才叫 局部变量
a=1;// 隐式全局变量
var b = 1;//全局变量

function aaa(){
    var c=1;// 局部变量
    d = 1;//隐式全局变量
}

if(true){
   var e=1;//全局变量
   }
alert(e);//可以被调用
// 由此可知,只用函数中的{} 才能降低作用域

18.2 作用域链

当查找变量的时候,会从当前上下文的变量对象中查找,如果没有找到就会从父级执行上下文的变量对象,即全局对象,这样由多个执行上下文的变量对象构成的链表就被称作为作用域( Scope Chain)

var num=0; //0级作用域
function aa(){
    var num=1; // 1级作用域
    function bb(){
        var num=2; //2级作用域
        function cc(){
            var num=3; //3级作用域
        }
    }
}

本章总结

  1. JavaScript常用的系统函数有三种,分别是parselnt()丶parseFloat()和isNaN()。
  2. 定义函数的方式有两种,分别是函数声明和函数表达式。
  3. 预解析可以将变量的声明提前,也可以将函数的声明提前
  4. 函数可以作为参数或返回值使用。
  5. 局部变量是在函数内部声明的变量(必须使用var),只能在函数内部访问它。其特点是可以在不同的函数中使用名称相同的局部变量。
  6. 全局变量是在函数外声明的变量,网页上的所有脚本和函数都能访问它。
  7. 隐式全局变量可以被删除,而全局变量不可以被删除。
  8. 闭包的作用:可以读取函数内部的变量;可以让这些变量的值始终保存在内存中。
  9. 闭包的缺点:由于闭包可以读取函数内部的变量,所以闭包可以在父函数外部改变父函数内部变量的值;由于闭包会使得函数中的变量都被保存在内存中,所以内存消耗很大,不能滥用闭包,否则会造成网页性能问题。另外,在IE低版本浏览器中,可能会导致内存泄漏。

练习小作业:

  1. 实现两个数的四则运算
  2. 重复字符串
  3. 实现点赞功能
  4. 弹出百度热搜索引
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Abcdzzr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值