JS函数进阶

这次的内容我会给大家详细介绍函数方面的内容

1.箭头函数:ES6新增的定义函数的方式,箭头函数是用来简化函数定义语法的。

- 箭头函数的语法: ()=>{}
()里面写形式参数,{}里面写函数体

        var fun1=(a,b)=>{console.log(a+b);}fun1(1,2);// 3

可以看到这样写的函数就相当于

        function fun2(a,b) {console.log(a+b);}fun2(1,2)//3

- 如果如果形参只有一个,形参外侧的小括号也可以省略,例如:

        var fun3=a=>{console.log(a);}fun3(1);//1

- 如果函数体中只有一句代码,并且代码的执行结果就是函数的返回值,函数体大括号可以省略,例如:

        var fun4=a=>a;console.log(fun4(5));//5var fun5=(a,b)=>a+b;console.log(fun5(5,1));//6

- 在箭头函数中,我们最需要注意的一点是:箭头函数不绑定this关键字,箭头函数中的this,指向函数定义位置的上下文this,请仔细看如下代码:

        var count=5;var objX={count:10,fun:()=>{console.log(this.count);}}objX.fun();//5

为什么是5?,首先全局作用域中定义了一个count=5,对象objX中定义了一个count=10,由于这里是箭头函数,里面的this会指向函数定义位置的上下文,在这里面就会指向objX这个对象,但是objX是一个对象不会产生作用域,就会指向全局作用域window(nodejs里为global),而count=5定义在全局作用域下面,所以输出5!

不使用箭头函数我们观察一下结果

       var count=5;var objX={count:10,fun:function() {console.log(this.count);}}objX.fun();//10

不使用箭头函数就会输出10,指向了这个对象。在后面我会详细给大家介绍各种不同调用方式的函数里面的this指向会有什么不同!

- 箭头函数不支持arguments,例如:

    function sum1() {return Array.from(arguments).reduce((x, y) => x + y, 0);}console.log(sum1(1, 2, 3,4));//10let sum2 = () => {return Array.from(arguments).reduce((x, y) => x + y, 0);};console.log(sum2(1,2,3,4));//报错
  • 箭头函数支持剩余函数。 例如:
  let sum3 = (...nums) => {return nums.reduce((x, y) => x + y, 0);};console.log(sum3(1, 2, 3));// 6

2.函数的4种定义方式:

  • 自定义函数(存在函数的提升,当函数一被定义就提升到顶层)
    例如:
        console.log(sum(1,2));//3function sum(num1,num2){return num1+num2;};console.log(sum(1,2));//3

这是我们最常见的函数定义方法,存在函数的提升,当我们自定义函数一旦声明就会提升到顶层!

  • 匿名函数(不存在函数的提升,只有在定义函数语句之后调用才有效,否则报错)
    例如:
        console.log(fun(1,2));//报错var fun=function (num1,num2){return num1+num2;};console.log(fun(1,2));//3

这里声明fun函数之前就调用了fun函数,由于不存在函数的提升,所以会报错,只有在声明之后再使用。

  • 构造函数( new Function(a,b,‘函数体’) ) 不存在函数提升
    例如:
       console.log(result(1,2));//报错var result=new Function("num1","num2","return num1+num2;");console.log(result(1,2));

Function构造函数前面的参数都为形式参数,最后一个参数始终为函数体!,如上面所示,除去最后一个参数都为形式参数,最后一个参数为函数体。

不存在函数提升,必须先声明后使用!

存在的问题:
1容易出错,不能智能感应
2.会被做2次解释,第一次是将它当成ECMAScript代码,第二次是解释传给构造函数的字符串,
不推荐大家使用这种方法

  • 箭头函数(不存在函数提升)
    箭头函数也是函数定义的方法 语法为()=>{},例如下面代码:
        console.log(jiantou(1,2));//报错var jiantou=(a,b)=>a+b;console.log(jiantou(1,2));

3.函数arguments属性

一个类数组对象(但不是 Array 的实例),因此可以使用中括号语法访问其中的元素。例如:

            function fun(a,b,c) {console.log(arguments instanceof Array);console.log(arguments);for(let i=0;i<arguments.length;i++){console.log(arguments[i]);}}fun(1,2,3)


请看结果,arguments不是一个Array实例,是一个类数组对象,可以用arguments[0]等方法访问里面的数据

在函数体里,arguments.length表示传入函数实参的个数,而函数本身的length属性是刻度的,它代表函数声明的实际参数的数量,例如:

function fn(a,b){console.log(arguments instanceof Array);//falseconsole.log(arguments instanceof Object);//trueconsole.log(arguments);console.log(arguments.length);//5console.log(fn.length);//2console.log(arguments.callee.length);//引用当前函数对象的length. 2}fn(1,2,3,4,5);

主要的是arguments属性可以表示实际传入函数参数的个数,函数自带length属性表示函数定义的形参个数。

4.函数参数问题

- 函数的参数传递 例如:

           function test(x,y='world'){console.log(x,y);}function test1(x,y){console.log(x,y);}test('hello');//hello worldtest('hello','hello');//hello hellotest1('hello')//hello undefinedtest1()//undefined undefined

请大家细品,如果形参里初始化了,不给这个形参传递值,这个形参就会输出初始化的值,如果给了这个形参传递值,这个形参就会使用传递过去的值。

如果形参没有初始化也没有传递值就会输出undefined

- 函数的参数的作用域 例如:

            let x='world';function test2(z,y=x){console.log(z,y);}test2('hello');//hello worldtest2();//undefined world//在函数参数里面y=x,x找不到向外面找。找到x的值//function test3(x,y=x){console.log(x,y);}test3('hello');//hello hellotest3();//undefiend undefiend//在函数里有x,y会找参数里面的x的值

请看test2函数 形参y=x,x的值在形参里没有,就会找函数外面x的值输出!
请看test3函数 形参里有x了,如果不给这个x赋值就会输出undefined,此刻y=x,y找到了x的值为undefined,所以y也为undefined,可以理解为一个就近原则,那里的x的值比较近,就去调用那里的x的值!

- 函数参数(…arg),这是个收集参数(剩余参数),写了这个收集参数后面不能再写其他参数! 例如:

             function test4(...arg){console.log(arg);console.log(arg instanceof Array);};test4('1','2',3,'tx');//['1','2',3,'tx'] trueconsole.log('a',...[1,2,3]);//把数组拆分//a 1 2 3

请大家细品,在函数参数里面写剩余参数,就会把接收到的参数变成一个数组,但是在输出语句中写剩余参数,就会把数组拆开!!!

请再看一个实例:

                function sum2(name,...scores){let r=scores.reduce((x,y)=>x+y,0); console.log(scores);console.log(`${name}总分为:${r}.`);};sum2('tx',10,10,10);//输出[10, 10, 10]//tx总分为:30.

相信大家都明白剩余参数的用法了!

5.函数的6种调用方式的this指向问题

- 自定义函数的this指向 例如:

         function f1(){console.log('写代码');console.log(this);//指向window};f1();//写代码 window

自定义函数里面的this 指向window全局

  • 对象里面的函数的this指向,例如:
         var obj={id:1,f2:function(){console.log('写代码');console.log(this);//指向这个对象}};obj.f2();//写代码 obj

对象里面的函数this 指向的是这个对象,但是对象里面的函数如果写出箭头函数就会指向全局,我们在前面已经看见过

- 构造函数以及原型的this指向,例如

        function myFriend(name,age){this.name=name;this.age=age;this.favoriteSport=function(){console.log('我喜欢打篮球');console.log(this);//指向了利用这个构造函数创造的实例对象}};myFriend.prototype={sing(){console.log('i can sing');console.log(this);//构造函数原型里面的this也指向了这个构造函数创造的实例对象},constructor:myFriend};myFriend.prototype.__proto__={dance(){console.log('i can dance');console.log(this);//构造函数原型里面的this也指向了这个构造函数创造的实例对象},constructor:myFriend.prototype};var tx=new myFriend('tx',20);tx.favoriteSport();tx.sing();tx.dance();

结果为
大家可以知道了,构造函数以及原型对象里面的this都会指向这个构造函数实例化对象,简单来说就是用这个构造函数创造的实例对象,谁调用了这个方法,这个方法里的this就指向谁!

如果大家有一点不清楚原型的话可以看看我之前的博客,希望大家多和我沟通交流!

- 绑定事件的函数的this指向 例如:

      var body=document.querySelector('body');var btn=document.createElement('button');btn.innerHTML='按钮';body.appendChild(btn);btn.onclick=function(){console.log('我按下了按钮');console.log(this);//this指向这个函数的调用者};

这里的this会指向这个函数的调用者,也就是这个按钮!

- 定时器函数的this 指向 例如:

setInterval(() => {console.log('定时器调用了');console.log(this);//指向window}, 5000);

定时器函数里面的this会指向window

- 立即执行函数的this指向 例如:

(function(num1,num2){console.log(num1+num2);console.log(this);//还是指向window})(1,2);

立即执行函数里面的this依旧会指向window

6. call()改变函数内的this指向

  • call(this,par,par)可以调用一个对象,也可以改变函数内的this指向,第一个参数为要改变的this的指向,后面的为参数!
    例如:
        var o={name:'tx'};function fn(a,b){console.log(this);console.log(a+b);}fn(1,2);//3fn.call(1,2);//Number{1} NaNfn.call(o,1,2);//第一个参数为改变的this指向,后面的为参数

第一个参数始终为指向,如果就写一个数字1 就指向数值对象

  • call的主要作用为继承 例如:
function Father(uname,age,sex){this.uname=uname;this.age=age;this.sex=sex;}function Son(uname,age,sex){Father.call(this,uname,age,sex)//调用Father函数,用call把Father函数里面的this改为son里面的this}var son=new Son('tx','20','male');console.log(son);


可以用call来继承

7.apply改变函数的this指向

- apply(thisArg,[argsArray])调用函数的方法,改变函数的this指向,第一个参数为要改变的this指向,第二个为传递的值,必须包含在数组
例如:

       var o1={name:'tn',};function fn1(name,age){console.log(this);console.log(name,age);};fn1();fn1.apply();fn1.apply(o1);//apply的参数必须写成数组的形式// fn1.apply(o1,'tx',20);//报错fn1.apply(o1,['tx',20]);


基本上和call用法一致,就是要用数值的形式传递参数

  • apply的主要用法是利用apply运用数学对象方法
        var arr=[1,2,3,99];console.log(Math.max(1,2));//2console.log(Math.max.apply(Math,arr));//99

这样更方便调用数学的方法!

8.bind()方法不会调用函数,但是能改变函数内部的this指向

bind(thisArg,arg1,arg2,…)
thisArg:在函数运行时指定的this
arg1,arg2:传递的其他参数
返回由指定的this值和初始化参数改造的原函数拷贝
bind()绑定后,不能再利用call,apply
例如:

 var o2={name:'tx',};function f3(a,b){console.log(this);console.log(a+b);};f3.bind(o2);//不输出 相当于一个新函数 var f=f3.bind(o2)f(1,2);


输出这个结果,bind()不会调用原来的函数 可以改变原来函数内部的this指向,返回的是原函数改变this之后产生的新函数
引用场景:如果有的函数我们不需要立即调用,又想改变这个函数内部的this指向,之前的call和apply都是会立即调用的

  • 在这里我给大家介绍一下偏函数

偏函数 ,固定一个函数的一个或者多个参数,
返回一个新的函数,这个函数用于接收剩余的参数
例如:

//给函数固定一个参数 生成新函数。新函数继续接收其他参数let sum=function(x,y){console.log(x+y);console.log(x);console.log(y);};let succ=sum.bind(null,1);//null不改变指向 1传xsucc(2)//2传入y

9.回调函数

被作为实参传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数,称为回调函数。 例如:

let data={x:10,y:20,show:function(how){how(this.x,this.y);},};data.show(function(a,b){console.log(`${a},${b}`);});data.show((a,b)=>{console.log(`${a}->${b}`);});


如何理解,首先下面2个函数都是作为实参传递给一个函数,并在该外部函数内被调用,用以来完成某些任务,这就是回调函数
在data对象里写了一个show方法,但是show方法的表示不明确,就可以用另一个函数当作形参传递给show方法来调用,例如数组里的foreach函数也是一个回调函数

10.函数闭包

  • 闭包的主要作用:延伸了变量的作用范围

  • 要认识闭包,我们首要要知道:变量作用域分为全局作用域和局部作用域,函数内部可以使用全局变量,函数外部不能使用局部变量,当函数执行完毕,这个函数作用域的局部变量会被销毁。

  • 我们还要知道,ActiveObject,当调用一个函数时会,会生成ActiveObject对象保存局部变量,当函数调用结束就会销毁ActiveObject对象

  • - 闭包的实质:可以理解为调用了局部作用域的变量,实质也就是利用另外一个函数的ActiveObject保存了之前函数的ActiveObject对象来保存之前函数的局部变量。

  • 闭包的概念:指有权访问另一个函数作用域中变量的函数一个作用域可以访问宁一个函数内部的局部变量

我会给大家详细介绍闭包!
例如:

function f1(num1,num2){var result=0result=num1+num2;console.log(result);};f1(1,2)// console.log(result); 报错 没有定义

这里一定是报错的,调用f1函数会生成 ActiveObject对象来保存result这个局部变量,当调用结束就会销毁掉ActiveObject对象,我们就得不到result这个局部变量!

但是我们可以利用闭包来得到局部变量!
例如:

function fn(){var num=10;function fun(){console.log(num);//在fun这个函数内部访问了fn的局部变量//产生了闭包//fun这个函数作用域访问了宁外一个函数fn里的局部变量num}fun();}fn();// 10

这就是闭包 实际上就是当调用 fn函数是生成ActiveObject(1)对象来保存局部变量 num的值,然后又调用 fun函数,fun函数也会生成ActiveObject(2)对象,这个ActiveObject(2)相当于引用了ActiveObject(1),就能保存ActiveObject(1)里面的局部变量值!

ActiveObject对象要存在引用必须有函数嵌套的存在!!!!
例如:

var scope="globel scope";function checkscope(){var scope="loacl scope";//当调用这个函数 生成Ao(x)保存局部变量function f(){return scope;//当调用这个函数生成Ao保存局部变量 }return f;//f为函数对象  对象当然存在Ao引用 相当于Ao引用了Ao(x) 就可以获取Ao(x)的局部变量//如果不存在嵌套的函数,没有其他引用指向这个绑定对象,它就会被当作拉基回收//如果存在嵌套的函数,每个嵌套的函数都各自对应一个作用域链,并且这个作用域链指向一个变量绑定对象。//最重要的是 对于上面这个情况://如果这个函数定义了嵌套的函数,并将它作为返回值返回或者存储在某处的属性里,//这时就会有一个外部引用指向这个嵌套的函数。//Ao它就不会被当做垃圾回收,并且它所指向的变量绑定对象也不会被当做垃圾回收。}var ff=checkscope();//函数调用结束,不存在局部变量,但是闭包让局部变量保留下来//ff变相于 引用了Ao Ao保存了局部变量 Ao在函数调用结束后就不会被删除//ff为一个函数对象 存在Ao引用Ao(x);console.log(ff());//local scope;}//它们可以获取出局部变量,一直保存下去,存在闭包

大概就是这个意思,如果不存在函数嵌套,当调用函数生成的AO就会被当场垃圾删除掉!

函数进阶的内容还没有给大家介绍完全,希望大家可以指出我的问题,和我多沟通交流,谢谢大家!

javascript函数进阶详细内容 函数闭包 箭头函数 call bind apply用法 偏函数 回调函数相关推荐

  1. send函数 获得已发送数据长度_蓝牙BLE开发1--起因与回调函数

    1 起因 笔者在开发蓝牙芯片CC2540,试图取实现大量数据的BLE透传功能,在认真学习了蓝牙4.0协议标准以及TI提供的SDK说明,我总结了两个发送函数,以及两个接收函数的位置. 两个发送函数包括 ...

  2. ajax回调函数提交表单,MVC3中ajax提交表单无法执行success回调函数,怎么办?

    js代码 function AjaxInitForm(formId, btnId, isDialog, urlId){ var formObj = $('#' + formId); var btnOb ...

  3. 带你学习javascript的函数进阶(二)

    上一篇文章分享了<带你学习Javascript中的函数进阶(一)>,今天继续学习javascript的函数进阶的内容. 文章目录 1 严格模式 1.1 什么是严格模式 1.2 开启严格模式 ...

  4. javascript之异步操作理解---回调函数,async,await以及promise对象

    javascript之异步操作理解---回调函数,async,await以及promise对象 概述 概述 写在前面:虽然平时做项目,但是发现自己写的代码还是很烂.最近接触了一个对性能要求比较高的项目 ...

  5. 理解与使用Javascript中的回调函数

    在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被"存储"在变量中,能作为函数参数被传递,能在函数中被创建, ...

  6. javascript回调函数(模式)原理和示例深入分析

                                                                                     广大网友读懂了我之前论述的javasc ...

  7. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  8. 【JavaScript】理解与使用Javascript中的回调函数

    在Javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用.既然函数实际上是对象:它们能被"存储"在变量中,能作为函数参数被传递,能在函数中被创建, ...

  9. bootstraptable 加载完成回调函数_牛皮了!头一次见有大佬把「JavaScript中的回调函数」详解得如此清晰明了...

    前言 callback,大家都知道是回调函数的意思.但是你对这个概念应该是模模糊糊.比如Ajax,你只知道去调用返回函数,如果对callback没有理解清楚,估计你在学习Node.js后会崩溃,因为c ...

最新文章

  1. 深入解析Spring MVC与Web Flow
  2. Asp.net MVC突然变慢,缓存消失的一种原因
  3. C# 7.1、7.2特性追踪
  4. redis stream学习总结
  5. jquery地图插件jvectorMap
  6. iOS开发---- 开发错误汇总及解决方法
  7. list control 应用(转载)
  8. 严重的“Access:7”供应链漏洞影响100多家厂商150多款联网设备等产品
  9. 语音识别揭秘:你的手机究竟有多理解你?
  10. 2022年ADB 命令知多少?一文2000字详细 ADB 命令大全来啦
  11. Win10开启IPv6
  12. linux集群环境搭建
  13. 二分类模型评价指标-AUC
  14. Swipe轮播插件的使用
  15. “AI+机器人”持续为多领域增“智”添“质”,开启效益增长飞轮
  16. 【转】工作3个月后个人对测绘专业的重新反思(重新思考武大测绘学科就业问题和读研问题)...
  17. vue2-组件化开发
  18. 用程序员计算机算进制,一文带你读懂计算机进制
  19. 使用 Parallel 提高 Linux 命令行执行效率
  20. 利用Python对接MySQL实现员工管理系统

热门文章

  1. 旅游商城小程序怎么开发?
  2. 同程旅行Hadoop集群跨机房迁移实践
  3. QT mainwindow.ui: Warning: The name ‘layoutWidget‘ (QWidget) is already in use, defaulting to ‘layou
  4. 那些年,我们一起写过的单例模式
  5. numpy.ix_的用法详解
  6. 计算机管理系统日志 mei,计算机软件毕业设计-教育局oa系统-公文管理的设计与实现.doc...
  7. 从0基础开发搜索引擎(一)-----实战项目
  8. 手把手教你十五分钟搭建个人博客网站
  9. Dhtml,html,xhtml的区别
  10. 2020年高教社杯全国大学生数学建模竞赛C题 第一问详细解答+代码