[TOC]

✊总有人要成为第一,为什么不是你。

2021年08月05日

工作重点

1 工作重点事项 进度 已完成
2 上午讲JavaScript函数 已完成
- [x]

| | 3 | 下午讲JavaScript函数 | 已完成 |
- [x]

| | 4 | 晚上总结 | 已完成 |
- [x]

|

遇到的问题和解决思路

遇到的问题

  1. 当知识点都理解后

  2. 解决思路

  3. 一定要细心细心细心,不要骄傲,不要大意


  4. 今日总结

    (1)函数

    ### (2)参数伪数组 ### (3)函数的创建 ### (4)自执行函数 ### (5)参数的返回值 ### (6)函数的类型 ### (7)作用域的优先级 ### (8)变量的声明提前

(9)函数的参数

    <script type="text/javascript"><br />            **/***<br />**             * 定义一个用来求两个数和的函数**<br />**             *  可以在函数的()中来指定一个或多个形参(形式参数)**<br />**             *  多个形参之间使用,隔开,声明形参就相当于在函数内部声明了对应的变量**<br />**             *  但是并不赋值**<br />**             */**<br />            <br />            function sum(a,b){<br />                console.log("a = " + a);<br />                console.log("b = " + b);<br />                console.log(a+b);<br />            }<br />            **/***<br />**             * 在调用函数时,可以在()中指定实参(实际参数)**<br />**             *  实参将会赋值给函数中对应的形参**<br />**             */**<br />            <br />//          sum(3,4);<br />            <br />           ** /***<br />**             * 调用函数时解析器不会检查实参的类型,**<br />**             *  所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查**<br />**             * 函数的实参可以是任意的数据类型**<br />**             */**<br />            <br />            //sum(123,"hello");<br />    <br />            **/***<br />**             * 调用函数时,解析器也不会检查实参的数量**<br />**             *  多余实参不会被赋值**<br />**             * 如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined**<br />**             * **<br />**             */**<br />//          sum(123,4343,"hello");<br />            //sum(123);//NaN<br />        </script>

JavaScript-函数(重点)

函数其实就是封装,把可以重复使用的代码放到函数中,如果需要多次使用同一段代码,就可以把封装成一个函数。这样的话,在你需要再次写这些代码的时候,你只需要调用一次函数就行了。

1.1 定函数的方式

1.1.1使用字面量的形式定义函数

ECMAScript中的函数使用function关键字来声明,后面跟一组参数及函数体。
函数的基本语法如下:
📝第八天 每日总结(JS函数) - 图1
以下是一个函数的示例:

function sum(num1, num2) {
    var num3 = num1 + num2;
     return num3;
 }

函数的调用:

这个函数的作用是把两个值加起来返回一个结果。函数必须通过调用才会执行,调用这个函数的代码如下:

var result = sum(5, 10); //15

1.1.2使用函数表达式声明函数(匿名函数)

📝第八天 每日总结(JS函数) - 图2

1.1.3使用Function构造函数声明函数

📝第八天 每日总结(JS函数) - 图3

1.1.4 自执行函数

先前咱们写的函数只有在调动的时候才会执行,如果像下面这样写的话,代码执行到这一行的时候,函数会自己执行(或者说自己调用自己)。

(function(a){

     console.log(a);

})("abc")

1.2 函数的返回值

函数在执行完return语句之后停止并立即退出。因此,

  • 位于return语句后面的任何代码永远都不会执行。
  • 一个函数中可以有多个return语句。
  • return语句也可以不带任何返回值。

1.3 函数的参数

ECMAScript函数不介意传递进来多少参数,也不在乎传进来的参数是什么数据类型。也就是说,即便你定义的函数只接收2个参数,在调用这个函数的时候也未必一定要传递2个参数。可以传递1个、3个甚至不传递参数,而解析器永远不会有什么怨言。

实际上,传递给函数的所有参数都被保存到了arguments对象中,arguments对象位于函数体内,与数组有点类似(但它并不是Array的实例),参数都被保存到了这个数组中。

有的人可能会问了,既然所有传递进来的参数都被保存到arguments数组中了,那么在声明函数的时候还声明参数干什么?

其实声明形参只是为了方便在函数体中引用传递进来的实参,但这不是必需的,在声明函数时也可以不声明形参。
📝第八天 每日总结(JS函数) - 图4

函数是一种数据类型

function fn() {}
console.log(typeof fn);---返回值是function
  • 函数作为参数

因为函数也是一种类型,可以把函数作为一个函数的参数,在一个函数中调用

function func1(theFunc){
  var r1=arguments[1]
  var r2=arguments[2] 
  theFunc(r1,r2)           
}
function func2(a,b){
  alert(a+"----"+b);
}
func1(func2,"aaa","bbb");
  • 函数做为返回值

因为函数是一种类型,所以可以把函数可以作为返回值从函数内部返回,这种用法在后面很常见。

function fn(b) {
  var a = 10;
  return function () {
    alert(a+b);
  }
}
fn(15)();

1.4 作用域

在 Javascript 中,作用域分为 全局作用域函数作用域

全局作用域

代码在程序的任何地方都能被访问,window 对象的内置属性都拥有全局作用域。

函数作用域

在固定的代码片段才能被访问

全局变量和局部变量

变量的作用域范围可分为全局变量和局部变量。

  • 全局变量:

在最外层声明的变量叫全局变量。

在函数体内部,没有使用var关键字声明的变量也是全局变量。(不推荐)

  • 局部变量:

在函数体内部,使用var关键字声明的变量叫作局部变量。

注意点: 局部变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁

词法作用域

变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。

在 js 中词法作用域规则:

  • 函数允许访问函数外的数据.
  • 整个代码结构中只有函数可以限定作用域.
  • 作用域规则首先使用提升规则分析
  • 如果当前作用规则中有名字了, 就不考虑外面的名字
var num = 123;
function foo() {
  console.log( num );
}
foo();
if ( false ) {
    var num = 123;
}
console.log( num ); // undefiend

作用域链(背会)

  • 只有函数可以制造作用域结构, 那么只要是代码,就至少有一个作用域, 即全局作用域。凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。

  • 将这样的所有的作用域列出来,可以有一个结构:在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

  • 作用域链:采取就近原则的方式来查找变量最终的值。

// 案例1:
function f1() {
    function f2() {
    }
}
var num = 456;
function f3() {
    function f4() {    
    }
}
// 案例2
function f1() {
    var num = 123;
    function f2() {
        console.log( num );
    }
    f2();
}
var num = 456;
f1();

*我可以

什么是作用域和作用域链,请解释一下?

作用域(scope)

在 Javascript 中,作用域分为 全局作用域局部作用域
全局作用域:代码在程序的任何地方都能被访问,window 对象的内置属性都拥有全局作用域。
局部作用域:在固定的代码片段才能被访问。

作用域链(scope chain)

在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

1.5 预解析

JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析过程和代码执行过程

预解析过程:

  1. 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
  2. 把函数的声明提升到当前作用域的最前面,函数声明代表函数整体,所以函数提升后,函数名代表整个函数
  3. 先提升var,再提升function。
  4. 预解析会把变量和函数的声明在代码执行之前执行完成。
  • 变量预解析
预解析也叫做变量、函数提升。
变量提升(变量预解析): 变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升。
  • 函数预解析
函数提升: 函数的声明会被提升到当前作用域的最上面,函数声明代表函数整体,所以函数提升后,函数名代表整个函数。
  • 特殊情况—函数表达式声明函数问题
函数表达式创建函数,会执行变量提升,此时接收函数的变量名无法正确的调用:
fn();
var  fn = function() {
    console.log('想不到吧');
}
结果:报错提示 ”fn is not a function"
解释:该段代码执行之前,会做变量声明提升,fn在提升之后的值是undefined;而fn调用是在fn被赋值为函数体之前,此时fn的值是undefined,所以无法正确调用

举例


  1. var x;
    console.log(x)
    x=3;
    

  2. var a = 1;
    function test(){
     console.log(a);
     var a = 1;
    }
    test();
    

  3. var b = 1;
    function fun1() {
     b = 2;
     function fun2() {
         var b = 3;
         console.log(b);
     }
     fun2();
     console.log(b);
    }
    fun1();
    console.log(b);
    

  4. var b = 1;
    function fun1(b) {
     b = 2;
        console.log(b);
    }
    fun1('yy');
    console.log(b);
    

  5. var b = 2;
    function test2() {
     window.b = 3;
     console.log(b);
    }
    test2();
    console.log(b);
    

  6. c = 5;
    function test3() {
     window.c = 3;
     console.log(c);
     var c;
     console.log(window.c);
    }
    test3();
    console.log(c);
    

自己总结

在调用函数时,浏览器每次都会传递进两个隐含的参数:
1.函数的上下文对象 this
2.封装实参的对象 arguments
- arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
- 在调用函数时,我们所传递的实参都会在arguments中保存
- arguments.length可以用来获取实参的长度
- 我们即使不定义形参,也可以通过arguments来使用实参,
只不过比较麻烦
arguments[0] 表示第一个实参
arguments[1] 表示第二个实参 。。。
- 它里边有一个属性叫做callee,
这个属性对应一个函数对象,就是当前正在指向的函数的对象

function loop(num1, num2, num3) {
// 返回结果,renturn 后面的代码是不会执行的
return num1 + num2 + num3;
console.log(num1 + num2 + num3);
// 如果一个函数没有return 默认返回 undefined
}
// value 是函数的返回值
var value = loop(“红烧肉”, “大盘鸡”, “米饭”);
console.log(value);

函数的类型是function

* 函数作用域
* - 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
* - 每调用一次函数就会创建一个新的函数作用域,他们之间是互相独立的
* - 在函数作用域中可以访问到全局作用域的变量
* 在全局作用域中无法访问到函数作用域的变量
* - 当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用
* 如果没有则向上一级作用域中寻找,直到找到全局作用域,
* 如果全局作用域中依然没有找到,则会报错ReferenceError
* - 在函数中要访问全局变量可以使用window对象

* 在函数作用域也有声明提前的特性,
* 使用var关键字声明的变量,会在函数中所有的代码执行之前被声明
* 函数声明也会在函数中所有的代码执行之前执行

变量的声明提前
- 使用var关键字声明的变量,会在所有的代码执行之前被声明(但是不会赋值),
但是如果声明变量时不适用var关键字,则变量不会被声明提前

函数的声明提前
- 使用函数声明形式创建的函数 function 函数(){}
它会在所有的代码执行之前就被创建,所以我们可以在函数声明前来调用函数
使用函数表达式创建的函数,不会被声明提前,所以不能在声明前调用

变量提升在函数的提升前面

fun();//能执行
fun2();报错
//函数声明,会被提前创建
function fun() {
console.log(“我是一个fun函数”);
}
//函数表达式,不会被提前创建
// 符合变量的提升规则
var fun2 = function () {
console.log(“我是一个fun2函数”);
};

当形参与局部变量相同且局部变量有赋值时,局部变量的赋值会覆盖形参的值。
var a = 1;
function fun(a) {
console.log(a);//2
console.log(arguments[0])//2
var a = 3;
console.log(a);//3
// 这里,当改变函数形参的值时,arguments对象中的值也会一起变动。
console.log(arguments[0])//3
}
fun(2);
console.log(a);//1

当形参与局部变量相同且局部变量只有声明时,参数不会被局部变量覆盖。
var a = 1;
function fun(a) {
console.log(a);//2
console.log(arguments[0])//2
var a;
console.log(a);//2 不是undefined
console.log(arguments[0])//2
}
fun(2);
console.log(a);//1