前端 — 初级阶段5(13)

ECMAScript核心语法结构之函数详解

1、函数的概念

函数是一段能够反复调用的代码块。接受输入的参数,不一样的参数会返回不一样的值。javascript

1.函数的定义

ECMAScript中是使用function关键字来声明,后面跟一组参数以及函数体。html

1) 声明函数前端

function sum (num1,num2){
     return num1 + num2;
 }
//能够先使用后定义。 解析器在执行环境中加载数据时,会率先读取函数声明。

2) 函数表达式:将一个匿名函数赋值给一个变量java

var sum = function (num1,num2){
       return num1 + num2;
  }
  //必须定义后使用(不然会报错)。到解析器执行到它所在的代码行,才会真正被解析执行

3) 构造函数(几乎不用)es6

var sum = new Funtion(
    'num1',
    'num2',
    'return num1 + num2'
) 
//等同于上面的声明函数

2.函数的调用(括号运算符)

函数能够经过其函数名调用,后面还要加上一对圆括号和参数(若是有多个参数,用逗号隔开)ajax

function sum(num1, num2) {
    return num1 + num2;
}
sum(1, 1)  //2

3.函数的参数

1.什么是参数数组

函数运行的时候,有时须要提供外部数据,不一样的外部数据会获得不一样的结果,这种外部数据就叫参数。闭包

function sum (num1,num2){
     return num1 + num2;
}
sum(1,2); //3
sum(1)    //NAN  num2 undefind;

2.参数的传递方式 - 按值传递app

(1) 基本类型值的传递如同基本类型变量的复制同样。
(2) 引用类型值的传递则如同引用类型变量的复制同样(将这个值在内存中的地址复制给了一个局部变量,所以这个局部变量的变化就会反在函数的外部)ide

//基本类型的传递
function addTen(num) { 
     num += 10; 
     return num; 
}
var count = 20; 
var result = addTen(count); 
alert(count); //20,没有变化
alert(result); //30

//引用类型的传递
var person = new Object(); 
function setName(obj) { 
    alert(person.name); //undefined
    obj.name = "Nicholas"; 
    alert(person.name)  //"Nicholas"
} 
setName(person); 
alert(person.name); //"Nicholas"

3.arguments对象(读取函数体内部全部参数)

  • arguments对象是一个类数组(有length属性,但没有数组的任何方法)
  • 能够编写无需指定参数个数的函数
var sum=function(){
    var sum=0;
    for(var i=0;i<arguments.length;i++){
        sum += arguments[i];
    }
    return sum;
}
sum(1,2,3)

4. 函数的返回值(return)

函数能够经过return语句跟要返回的值来实现返回值。函数在执行完return语句以后中止并当即退出。所以,在return语句以后的任何代码永远都会回执行。

function sum(num1, num2) {
    return num1 + num2;  
    alert('Hello world') //永远不会执行
}

return语句不是必须的,若是没有的话,该函数就不会返回任何值,或者说返undefined;

function sum(num1, num2) {
    alert (num1 + num2)  
}     //undefined

5.函数没有重载(函数的重复声明)

函数重载:在其余语言(Java)中能够为一个函数体编写两个定义,只要定义的签名(接收的参数的类型和参数不一样便可))
在ECMASctipt中若是同一个函数被屡次声明,后面的声明就会覆盖前面的声明。

function addSomeNumber(num){
    return num + 100;
}
function addSomeNumber(num) { 
   return num + 200; 
}
var result = addSomeNumber(100); //300

6.函数的属性和方法

(1) name属性(返回name的名字)

function sum (){}
 sum.name // 'sum'

(2) length 属性

函数的length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。

function f(a,b){
    return f.length 
}
f(1,2,3,4)  //2 ==> length属性就是定义是的参数个数。无论调用时传入了多少参数,length属性的始终等于2

(3)apply()和call()
每一个函数都包含两个非继承而来的方法:apply()和和call()。这两个方法的用途都是在特定的做用域中调用函数,实际上等于设置函数体内this对象的值。
apply()方法接收两个参数,一个是在其运行函数体内的做用域,另外一个是参数数组。其中第二个参数是数组或者arguments对象。
call()方法接收两个参数,第一个参数是this值没有变化,变化的是其他参数都是直接传递给函数(的参数必须逐个列举出来)

1.传参

function sum(num,num2){
      return num1 + num2;
  }
  function callSum(){
      return sum.call(this,num1,num2);
  }
  alert(callSum(10,10)); //20

2.扩充函数赖以运行的做用域 (改变this指向的值)

var color = 'red';
var o = {
    color: 'blue'
}
sayColor.call();       //red(默认传递参数this)
sayColor.call(this);   //red
sayColor.call(window); //red 
sayColor.call(o);      //blue

2、函数的做用域和做用域链

1.做用域

(1)什么是做用域
做用域(scope)指的是变量存在的范围。 在es5中的,Javascript只有两种做用域:一种是全局做用域,变量在整个程序中一直存在,全部地方均可以读取;另外一种是函数做用域,变量只在函数内部存在。(es6中又新增了块级做用域)
全局做用域能够在代码的任何地方都能被访问

var color1 = 'blue';     
function colorFn (){
    var color2 = 'red';  
    color3 = 'yellow';  
    console.log(color1); 
    console.log(color2);
    console.log(color3);
}
colorFn();
console.log(color1);   //'blue' 
console.log(color2);   // error
console.log(color3);   //'yellow'

//函数changeColor()的做用域链包含两个对象:它本身的变量对象(定义着arguments对象)和全局的变量的对象

2.做用域链

1.当代码在一个执行环境中执行时,就会建立一个做用域链。

2.做用域链的用途,是为了保证对执行环境有权访问的全部变量和函数的有序的访问。即全局做用域和局部做用域的变量的访问权限是由做用域链决定的。

3.做用域链的是从当前的执行环境的变量对象在这个环境中能够访问的变量对象开始,到全局执行环境的变量对象结束。(在这个环境中能够访问的变量)

4.内部环境能够经过做用域链访问全部外部环境,可是外部环境不能访问内部环境中的任何变量和函数。

5.标示符(变量、函数、属性的名字,或者函数的参数)的解析是沿着做用域链一级一级的搜索标示符的过程。搜索过程始终从做用域链的前端开始,而后逐级的向后回溯,直到找到标识符(若是找不到标识符,一般会报错)。

//在局部做用域中定义的变量能够在局部环境中与全局变量中互换使用
var color = 'blue';
function changeColor(){
     var anotherColor = 'red';
     function swapColors(){
          var tempColor = anotherColor;
          anotherColor = color;
          color = tempColor;
          //这里能够访问color、anotherColor和tempColor
      }
      //这里能够访问color和anotherColor,但不能访问tempColor
      swapColors();
}
//这个只能访问color;
changeColor();

图片描述

3、函数的其余知识点

1.闭包函数

(1) 什么是闭包函数
闭包函数是指有权访问另外一个函数做用域中的变量的函数
(2) 建立闭包的常见方式,就是在一个函数内部建立另外一个函数

//在函数内部能够读取函数外部的变量
var localVal = 30;
function outer(){
    return localVal;
}
outer();//30

//在函数外部天然没法读取函数内的局部变量
function outer(){
     var localVal = 30;
}
alert(localVal);//error

//经过闭包函数来读取读取outer函数内部的变量
function outer(){
     var localVal = 30;
        function inner(){   
          alert(localVal);
     }
     return inner; 
}
var func = outer();
func();//30

(3) 闭包的优缺点

1) 使用闭包的好处是不会污染全局环境,方便进行模块化开发,减小形参个数,延长了形参的生命周期
2) 闭包会把函数中变量的值保存下来,供其余函数使用,这些变量会一直保存在内存当中占用大量的内存,使用不当会形成内存泄漏

2.回调函数

(1) 什么是回调函数(做为值的函数)

回调函数也被称做高阶函数,是一个被做为参数传递给另外一个函数的函数。
function someFunction(someNum,callback){
    return callback(someNum);
}
function add10(){
    return num + 10;
}
someFunction()

(2) 应用场景实例:

1.数组中的一些方法 sort()、reverse()、forEach()、filter()等
     var friends = ["Mike", "Stacy", "Andy", "Rick"];
     friends.forEach(function (eachName, index){
        console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
     }); 
       
2.事件的绑定
     //原生绑定事件
    document.body.addEventListener("cilck",function(){
        alert("body Clicked");
    });
    
    //jq
    $("#btn_1").click(function() {
      alert("Btn 1 Clicked");
    }); 
     
3.ajax的请求
    function getAjax (options, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('get', options.url);
        xhr.onreadystatechange = function(){
            if(xhr.readyState == 4){
                if(xhr.status == 200){
                    callback && callback(xhr.responseText);
                }
            }
        };
        xhr.send();
    }

3.递归函数

1.什么是递归函数
递归函数是一个在函数经过名字调用自身的状况下构成的。递归函数必需要有边界条件

function factorial(num){
   if(num <= 1){
       return 1
   }else {
       return num * factorial(num - 1);
   }
}

2.arguments.calls()是一个指向正在执行的函数指针 (严格模式下不支持)
//当函数名发生改变时会致使代码出错

var anotherFactorial = factorial; 
factorial = null; 
alert(anotherFactorial(4)); //出错!

function factorial(num){
  if(num == 1){
      return 1;
  } else {
      return num * arguments.callee(num-1)
  }
}

3.严格和非严格都有效

var factorial = (function f (num){
        if(num == 1){
            return 1;
        } else {
            return f(num-1)
        }
   })

4.自执行函数

(1)什么是执行函数
当即执行函数,就是在定义的时候当即执行的函数,执行完之后释放,包括函数内部的全部变量(执行完毕,当即销毁其做用域链)

(2)当即执行函数的写法

1.经常使用写法
    (function(){
    })();

2.w3c建议方式
    (function(){
    }());

3.错误写法
    function(){
    }();
  • 函数声明后面不能跟花括号(js会把function关键字当作一个函数声明的开始)
  • 函数表达式的后能够跟圆括号。要将函数声明转变成函数表达式
  • 除圆括号外,!、+、-、=等运算符,都将函数声明转换成函数表达式

    !function(){}();
      +function(){}();
      -function(){}();
      var fn=function(){}();

(3) 模仿块级做用域

function outputNumbers (count){
    for(var i=0; i<count;i++){
        alert(i)
    }
    var i;
    alert(i);
    //计数 i从有定义开始,在函数内部就能够访问它,即便从新声明一个变量也不会改变它的值(不会改变?)。
}
function outputNumbers (count){
    (function(){
        for(var i=0; i<count;i++){
            alert(i)
        }
    })(); //变量会在当即执行结束后会被当即销毁
    alert(i);
}
//能够避免过多全局变量和函数致使命名冲突;这个做用域里面的变量,外面访问不到(即避免「变量污染」)。

3.应用场景

<ul class="box">
    <li>0</li>
    <li>1</li>
    <li>3</li>
    <li>4</li>
</ul>
var list = document.getElementsByTagName('li');
for (var i = 0, len = list.length; i < len; i++) { 
    (function(i){   //为了得到不一样的i值,使用当即调用函数
        list [i].onmouseover = function() {
            console.log('index is :' + i);
        }
    })(i);
}

参考:
JavaScript高级程序设计(第3版)
https://wangdoc.com/javascrip...
https://www.cnblogs.com/caoru... JavaScript函数之做用域 / 做用链域 / 预解析
https://www.cnblogs.com/bucho... JavaScript中做用域和做用域链的简单理解(变量提高)
https://www.cnblogs.com/jingw... 闭包
https://www.cnblogs.com/langq...
https://www.cnblogs.com/mengf...
https://www.cnblogs.com/mafei... 当即执行函数
https://baijiahao.baidu.com/s... 当即执行函数

相关文章
相关标签/搜索