函数       

实现特定功能的 n 条语句封装体。

1. 创建一个函数对象

var myFunc = new Function();    // typeof myFunc 将会打印 function
  • 将要封装的代码以字符串的方式传递给构造函数

    • var aFunc = new Function("console.log('Hello!');");
      console.log(aFunc);
      // 打印:
      "
      function anonymous(){
      console.log("Hello!");
      }
      "
  • 使用函数声明来创建一个函数
    • function myFunc(){
      console.log("Hello myFunc !");
      }
      // 会 函数声明 提升
  • 使用函数表达式来创建一个函数
    • var myFunc = function(){
      console.log("Hello myFunc ! ");
      };
      // 会 变量声明 提升
    • var myAdd = function(a,b){                // 传递多个参数使用,隔开
      console.log(a+b);
      };

      注意:函数解析器不会检查实参的类型;    多余的参数将不会被使用;    缺少的参数定义为 undefined

2. 函数的返回值        外界需要函数处理的值

使用 return 关键字返回指定结果,并结束函数。

不写 return 默认为 return ;        此时的函数返回值为 undefined

3. 函数的参数        当无法确定 n 个变量的值时, 使用 n 个参数 传递____形参

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

    • length 属性就是定义时的参数个数。不管调用时输入了多少个参数,length 属性始终等于 形参个数

      function f(a, b){}
      f.length //
  • 同名参数
    • 如果有同名的参数,则取最后出现的那个值

      function f(a, a) {
      console.log(a);
      } f(1, 2) //
      f(1) // undefined // 这时,如果要获得第一个a的值,可以使用arguments对象。
      function f(a, a) {
      console.log(arguments[0]);
      } f(1) //

只有函数调用时,才能动态确定 this 的指向

.call(obj, 12, 13);

.apply(obj, [12, 13]);

.bind(obj, 12, 13);         重构建函数

不会立即执行函数,而是会创建一个新函数,新函数的 this 指向 obj,参数也被传递

也不会修改原函数的 this

  • arguments 实参列表对象             只有数组的 length 属性,能够通过 index 读写数据

    • 由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。
    • 包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
      var f = function (one) {
      console.log(arguments[0]);
      console.log(arguments[1]);
      console.log(arguments[2]);
      } f(1, 2, 3)
      //
      //
      //
      • 正常模式下,arguments对象可以在运行时修改。

        var f = function(a, b) {
        arguments[0] = 3;
        arguments[1] = 2;
        return a + b;
        } f(1, 1) //
      • 严格模式下,arguments对象是一个只读对象,修改它是无效的,但不会报错。
        var f = function(a, b) {
        'use strict'; // 开启严格模式 arguments[0] = 3; // 无效
        arguments[1] = 2; // 无效
        return a + b;
        } f(1, 1) //
    • 通过 arguments 对象的 length 属性,可以判断函数调用时到底带几个参数。
    • 虽然 arguments 很像数组,但它是一个对象。数组专有的方法(比如 slice 和 forEach),不能在 arguments 对象上直接使用。
      • 如果要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组。下面是两种常用的转换方法:

        //slice方法
        var args = Array.prototype.slice.call(arguments);

        //逐一填入新数组。
        var args = [];
        for (var i = 0; i < arguments.length; i++) {
        args.push(arguments[i]);
        }
    • arguments.callee 属性,返回它所对应的原函数
      • var f = function () {
        console.log(arguments.callee === f);
        } f() // true
        // 可以通过arguments.callee,达到调用函数自身的目的。
        // 这个属性在严格模式里面是禁用的,因此不建议使用。
  • 实参可以是任意数据类型
  • 函数参数不是必需的,Javascript 允许省略参数
    • 运行时无论提供多少个参数(或者不提供参数),JavaScript 都不会报错。省略的参数的值就变为 undefined
    • 但是,没有办法只省略靠前的参数,而保留靠后的参数。如果一定要省略靠前的参数,只有显式传入 undefined
      function f(a, b) {
      return a;
      } f( , 1) // 报错 SyntaxError: Unexpected token ,(…)
      f(undefined, 1) // undefined
  • 函数被称为 "第一等公民"
    • 将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。
  • 当数据有规律,有必要的话可以存到一个对象里,传入函数。
    • var sun = {
      name:"孙悟空",
      gender:"男",
      age:600
      }; function sayHello(obj){
      document.write("大家好,我是"+obj.name+", "+obj.gender+", "+obj.age+"岁了");
      } sayHello(sun);
  • 实参还可以是函数
    • function haha(someFunc){
      someFunc();
      } haha(sayHello);
  • 返回值可以对象
  • 返回值也可以是函数
  • break;    用于终止函数
  • continue;    用于终止本次循环,直接开始下一次循环
  • return;    用于设置返回值,也会马上结束函数

4. 函数的属性和方法

  • .name 属性,返回函数的名字

    • function f1() {}
      f1.name // "f1" var f2 = function () {};
      f2.name // "f2" var f3 = function myName() {};
      f3.name // 'myName'
  • .toString() 方法返回一个字符串,内容是函数的源码
    • function f() {
      a();
      b();
      c(); // 这是注释
      } f.toString()
      // function f() {
      // a();
      // b();
      // c(); // 这是注释
      // }

5. 作用域(scope)   

变量存在的范围。

不同作用域的同名变量不会冲突

  • 在 ES5 的规范中,Javascript 只有两种作用域:

    • 全局作用域    全局变量(global variable)

      • 在函数外部声明的变量,变量在整个程序中一直存在,所有地方都可以读取,甚至可以在函数内部读取;
    • 函数作用域    局部变量(local variable)
      • 在函数内部定义的变量,变量只在函数内部存在,外部无法读取。
      • 作用域 是静态的 在函数创建时,就已经确定了,并不在变化,且一直存在
      • 函数上下文 是动态的,在调用函数时创建并确定,在函数执行完了就被释放
      • var v = 1;
        
        function f(){
        var v = 2;
        console.log(v);
        } f() //
        v //
      • 只有函数作用域 和 全局作用域 ,没有其他作用域
  • 必会面试题
  • function Foo() {
    getName = function () { alert (1); };
    return this;
    }
    Foo.getName = function () { alert (2);};
    Foo.prototype.getName = function () { alert (3);};
    var getName = function () { alert (4);};
    function getName() { alert (5);} //请写出以下输出结果:
    Foo.getName();
    getName();
    Foo().getName();
    getName();
    new Foo.getName();
    new Foo().getName();
    new new Foo().getName();
  • 分析:
  •     var getName;
    window.getName = function () { alert (4);}; function Foo() { // window.Foo
    getName = function () { alert (1); }; // 覆盖 window.getName
    return this;
    };
    window.Foo.prototype.getName = function () { alert (3);};
    window.Foo.getName = function () { alert (2);}; //请写出以下输出结果:
    Foo.getName(); // window.Foo.getName() 2
    getName(); // window.getName() 4 // this.getName() window.getName() 1 注意是在什么时候被覆盖的
    Foo().getName();
    getName(); // window.getName() 1
    new Foo.getName(); // window.Foo.getName() 2 // 注意 new 返回的是对象 直接调用返回看return
    // f.getName() window.f.getName() window.Foo.prototype.getName 3
    new Foo().getName();

    new new Foo().getName(); // new window.f.getName() 3
  • ES6 又新增了块级作用域

6. 闭包 closure

① 嵌套: 作为函数里面的 函数 fn2

② fn2 引用了外部函数的局部变量

外部函数 被调用执行时产生闭包,在 new 的时候也相当于执行了____实质是在 第一个满足以上条件 的函数被解析到是,产生闭包

此时就产生了一个闭包____ fn2.Scopes[0] = fn2.closure————包含了被引用局部变量的一个"对象",存在于嵌套的内部函数中,可以使用调试工具查看到。

其中 [[Scopes]] 是由底层 c/c++ 实现

  • 一个外层函数,嵌套多个函数,这几个内部函数 共用一个 闭包,该闭包不会在外层函数执行完后销毁,也就意味着延长了变量的存活时间
  • 闭包内的变量,无法直接操作,只能通过内层函数间接操作闭包内的变量
  • 闭包存在于 内部函数 fn2 中, 只要 fn2 存在,则闭包存在____当再也没有 变量 指向这个 fn2 ,则 fn2 不存在,闭包也就不存在了

缺点:

由于闭包的存在,未释放,导致占用内存时间变长。易造成内存泄漏

怕的就是出现 非常多个闭包

解决:

能不用闭包就不用闭包

及时释放____让 指向含闭包函数的 变量,指向 null

  • 作用有

    • 使得可以 在函数外部 间接操作 函数内部的变量。
    • 让这些变量始终保持在内存中, 不会在函数调用结束后,被垃圾回收机制回收。
    • 封装对象的私有属性和私有方法。
      • function Person(name) {
        var _age;
        function setAge(n) {
        _age = n;
        }
        function getAge() {
        return _age;
        } return {
        name: name,
        getAge: getAge,
        setAge: setAge
        };
        } var p1 = new Person('张三');
        p1.setAge(25); // 操作的是 闭包中的 _age
        p1.getAge() // 访问的是 闭包中的 _age
        // 上面代码中,函数Person的内部变量_age,通过闭包getAgesetAge,变成了返回对象p1的私有变量。

        注意

        • 外层函数每次运行,都会生成一个新的闭包,而这个闭包又会保留外层函数的内部变量,所以内存消耗很大。

        • 因此不能滥用闭包,否则会造成网页的性能问题。

  • 是Javascript 语言的一个难点,也是它的特色。
  • 闭包最大的特点,就是它可以 “记住” 诞生的环境,比如 fn2 记住了它诞生的环境 fn1 的内部变量。在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
  • 很多高级应用都要依靠闭包实现。
  • 理解闭包,首先必须理解变量作用域。
  • "作用域链"(scope chain)
    • JavaScript 语言特有的结构
    • 子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
  • 正常情况下,得到函数内的局部变量是办不到的

    • 只有通过变通方法才能实现
    • 那就是在函数的内部,再定义一个函数。
    • 既然子函数可以读取父函数的局部变量,那么只要把子函数作为返回值,我们不就可以在父函数外部读取它的内部变量了吗!
      function f1() {
      var n = 999;
      function f2() {
      console.log(n);
      }
      return f2;
      } var result = f1();
      result(); // 999
      // 函数f1的返回值就是函数f2,由于f2可以读取f1的内部变量,所以就可以在外部获得f1的内部变量了
    • 闭包就是返回的 子函数 f2,即能够读取其他函数内部变量的函数。
  •  请看下面的例子,闭包使得内部变量记住上一次调用时的运算结果。

    • function createIncrementor(start) {
      return function () {
      return start++;
      };
      } var inc = createIncrementor(5); // 执行了 外层函数,然后销毁了 函数上下文,即 所有外层函数的变量都销毁了 inc(); // inc(); // inc(); //

7. 立即调用的函数表达式(IIFE)

自调用匿名函数

____封装时, 隐藏函数内部具体实现, 防止外部修改函数内的代码

____防止污染外部命名空间

    • 以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义语句,避免出现错误。

这就叫做“立即调用的函数表达式”(Immediately-Invoked Function Expression),简称 IIFE。

    • 目的有两个:

      • 一是不必为函数命名,避免了污染全局变量;
      • 二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量。
        // 写法一
        var tmp = newData;
        processData(tmp);
        storeData(tmp); // 写法二 更好,因为完全避免了污染全局变量。
        (function(w){ //
        var tmp = newData;
        processData(tmp);
        storeData(tmp);
        }(window)); // 方便对代码进行压缩, 变量 window 改成 w, 减小代码体积
  • 圆括号()是一种运算符,跟在函数名之后,表示调用该函数。    比如,isNaN() 就表示调用 isNaN 函数。
  • 当 function 出现在行首,为了不让引擎将其理解成一个表达式。最简单的处理,就是将其放在一个圆括号里面。

 8. 回调函数

自定义的,不被自己调用的,达到某种条件后被某对象调用了。

异步加载通知机制: 

所有异步代码,不能立即执行

只有等主进程执行完所有代码后,才会执行 异步代码

  • setTimeout(function(){
    console.log("异步代码");
    }, 0); console.log("主进程代码");
    // 打印:
    // 主进程代码
    // 异步代码

9. eval 命令

接受一个字符串作为参数,并将这个字符串当作语句执行。

  • eval('var a = 1;');
    a //
  • 如果参数字符串无法当作语句运行,那么就会报错。    // Uncaught SyntaxError: Invalid or unexpected token
  • 如果eval的参数不是字符串,那么会原样返回。
  • eval 没有自己的作用域都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。
  • JavaScript 规定,如果使用严格模式eval内部声明的变量,不会影响到外部作用域。
    • 不过,即使在严格模式下,eval依然可以读写当前作用域的变量。

      (function f() {
      'use strict';
      var foo = 1;
      eval('foo = 2');
      console.log(foo); //
      })()

      所以一般不推荐使用。通常情况下,eval 最常见的场合是解析 JSON 数据的字符串,不过正确的做法应该是使用原生的JSON.parse方法

  • eval 的别名调用的形式五花八门,只要不是直接调用,都属于别名调用,因为引擎只能分辨eval()这一种形式是直接调用。
    eval.call(null, '...');
    window.eval('...');
    (1, eval)('...');
    (eval, eval)('...'); // 上面这些形式都是eval的别名调用,作用域都是全局作用域。

内存溢出

需要的内存,大于现有内存

内存泄漏

资源占有一片空间,而未曾释放,导致这片内存无法被使用

及时 obj = null; 来释放内存

  • 意外的全局变量

在函数中, 未使用 var 关键字定义变量, 导致直接声明了一个全局变量

  • 没有及时清理的 定时器 或者 回调函数
  • 闭包

10. 属性描述符

(60)Wangdao.com第十天_JavaScript 函数_作用域_闭包_IIFE_回调函数_eval的更多相关文章

  1. 【授课录屏】JavaScript高级(IIFE、js中的作用域、闭包、回调函数和递归等)、MySQL入门(单表查询和多表联查)、React(hooks、json-server等) 【可以收藏】

    一.JavaScript授课视频(适合有JS基础的) 1.IIFE 2.js中的作用域 3.闭包 4.表达式形式函数 5.回调函数和递归 资源地址:链接:https://pan.baidu.com/s ...

  2. (62)Wangdao.com第十天_JavaScript 变量的作用域

    在 js 中有两种作用域:全局作用域,局部作用域. 全局作用域 直接写在 <script> 标签中的变量和方法. 在网页打开时创建,在网页关闭时销毁. 全局作用域有一个全局对象 windo ...

  3. Android 回调函数的理解,实用简单(回调函数其实是为传递数据)

    作者: 夏至,欢饮转载,也请保留这段申明 http://blog.csdn.net/u011418943/article/details/60139910 一般我们在不同的应用传递数据,比较方便的是用 ...

  4. JS---最终版本--封装缓动(变速)动画函数---增加任意多个属性&回调函数&层级&透明度

    封装缓动(变速)动画函数---增加任意多个属性&回调函数&层级&透明度 相较之前的,增加了2个判断,第一个判断是不是透明度,第二个判断是不是zindex, 都不是,就只是普通属 ...

  5. 计算机基础,Python - 回调函数,使用装饰器注册回调函数

    1. 参考: https://en.wikipedia.org/wiki/Callback_(computer_programming) https://developer.mozilla.org/e ...

  6. (82)Wangdao.com第十六天_JavaScript 异步操作

    异步操作 单线程模型 指的是,JavaScript 只在一个线程上运行 也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待 注意,JavaScript 只在一个线程上运 ...

  7. 学习 easyui 之二:jQuery 的 ready 函数和 easyloader 的加载回调函数

    Ready 事件不一定 ready 使用 easyloader 的时候,必须要注意到脚本的加载时机问题,easyloader 会异步加载模块,所以,你使用的模块不一定已经加载了.比如下面的代码. &l ...

  8. javascript 函数和作用域(闭包、作用域)(七)

    一.闭包 JavaScript中允许嵌套函数,允许函数用作数据(可以把函数赋值给变量,存储在对象属性中,存储在数组元素中),并且使用词法作用域,这些因素相互交互,创造了惊人的,强大的闭包效果.[upd ...

  9. 前端笔记知识点整合之JavaScript(四)关于函数、作用域、闭包那点事

    一.自定义函数function 函数就是功能.方法的封装.函数能够帮我们封装一段程序代码,这一段代码会具备某一项功能,函数在执行时,封装的这一段代码都会执行一次,实现某种功能.而且,函数可以多次调用. ...

随机推荐

  1. JDK8- interface接口可以写方法体了-- 惊 dt.jar包等

    基本上所有的Java教程都会告诉我们Java接口的方法都是public.abstract类型的,没有方法体的. 但是在JDK8里面,你是可以突破这个界限的哦. == 一个类实现某个接口,必须重写接口中 ...

  2. 设计模式六: 模板方法(Template Method)

    简介 模板方法属于行为型模式的一种. 实现层面上, 在抽象类中定义了算法或流程的骨架, 将其中易变的部分延迟到子类实现, 也就是允许它的子类实现其中的某些步骤. 模板方法适用于算法不变, 但算法中某些 ...

  3. Python日志模块logging用法

    1.日志级别 日志一共分成5个等级,从低到高分别是:DEBUG INFO WARNING ERROR CRITICAL. DEBUG:详细的信息,通常只出现在诊断问题上 INFO:确认一切按预期运行 ...

  4. k64 datasheet学习笔记26--Oscillator (OSC)

    1.前言 OSC模块是一个晶体振荡器. 该模块使用晶体或谐振器与外部连接,为MCU产生一个参考时钟. 主要为下图红色框住的部分 2.特性和模式 Supports 32 kHz crystals (Lo ...

  5. Linux 之 rsyslog+mysql+LogAnalyzer 日志收集系统

     作者:邓聪聪 LogAnalyzer 是一个 syslog 和其他网络事件数据的 Web 前端工具,提供简单易用的日志浏览.搜索和基本分析以及图表显示 由于公司部分项目需求使用日志记录系统,随笔记录 ...

  6. WPF 窗口去除顶部边框(正宗无边框)

    最近在做一个大屏展示视频图片的项目,功能并不复杂,半天的工作量吧,一开始同事采用的Unity3D进行开发,但是里面要播放4K视频,Unity 的短板就是视频的播放了,今晚就要交付了,我一早就来公司,决 ...

  7. mysql 的crud操作(增删改查)

    1.mysql添加记录 --添加记录的语法(可添加单条记录或者多条记录),INTO是可以省略的,字段名也可以省略的,但是如果省略的话,后面对应的value的值就要全部填写 INSERT [INTO] ...

  8. java学习 之 java基本数据类型

    java 8个基本数据类型 public class BasicDataType { public static void main(String args[]) { //数据类型 //Byte Sy ...

  9. iOS制作自己的Framework框架

    1.新建工程选择iOS —> Cocoa Touch Framework 2.进入工程将工程自带的文件干掉 3.导入自己所需的文件 4.4.TARGETS —> Build Setting ...

  10. php判断是不是手机端访问

    最笨方法自己亲测! if (isset($_SERVER['HTTP_USER_AGENT'])) { $clientkeywords = array('iphone', 'android', 'ph ...