你疏漏的 JS 函数硬核知识?这里帮你总结了

重点

更多前端知识 诚邀各位前端从事者爱好者加入前端大佬技术交流社区,本社区主要分享技术栈、个人心得、技术交流、问题解惑等前端体系交流

点击下方文字加入

前端大佬技术交流社区

1. 函数的定义和调用

1.1 函数的定义方式

  1. 方式1 函数声明方式 function 关键字 (命名函数)

    function f1() {console.log('命名函数')
    }
    
  2. 方式2 函数表达式(匿名函数)

    var f2 = function () {console.log('匿名函数')
    }
    
  3. 方式3 new Function()

    /*参数1:函数形参参数2:函数形参参数3:函数体*/
    var f3 = new Function('m', 'n', 'console.log(m-n)')
    // 传入实参
    f3(4,2)

注意

  • Function 里面参数都必须是字符串格式
  • 第三种方式执行效率低,也不方便书写,因此较少使用

1.2 函数的调用

 // 普通函数:命名函数和匿名函数
function f1() {console.log('人生何处不相逢');
}
var f2 = function () {console.log('寒江孤影,江湖故人,相逢何必曾相识');
}
// 对象中的函数,专业点叫做方法,通过 对象.方法 方式调用
var Person = {speak: function () {console.log('人生处处是歌声');}
}
// 构造函数:通过 new 关键字调用
function Star() { }
new Star()
// 事件处理函数:事件触发时被调用
element.onclick = function () { }
// 定时器函数:由定时器选择时机调用
setInterval(function () { }, 1000)
// 立即执行函数:立即调用
(function () {console.log('first off')
}())

2.this

2.1函数内部的this指向

这些 this 的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this 的指向不同

一般指向我们的调用者.

在这里插入图片描述

2.2改变函数内部 this 指向

2.2.1 call方法

call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向

应用场景: 经常做继承.

var o = {name: 'andy'
}function fn(a, b) {console.log(this);console.log(a+b)
};
fn(1,2)// 此时的this指向的是window 运行结果为3
fn.call(o,1,2)//此时的this指向的是对象o,参数使用逗号隔开,运行结果为3

以上代码运行结果为:

在这里插入图片描述

复习使用this实现继承

2.2.2 apply方法

apply 方法的用于与call 非常类似,区别在于,不能以参数列表的形式传递实参,必须以数组的形式传递

         var o = {name: 'andy'}function fn(a, b) {console.log(this);console.log(a + b)};// 普通调用fn(1, 2)// call调用,将函数中的this修改为o对象fn.call(o, 1, 2)// apply 调用,区别在于参数列表必须以数组形式传递fn.apply(o,[1,2])

可以看到,我们传入函数的实参是数组,但是函数内部会自动展开为参数列表

利用这一特性,我们可以结合 Math.max 方法求数组的最大值或者最小值

Math.max 方法可以求一组数字的最大值,语法如下

Math.max(1,2,3,4,5)

参数为参数列表形式

利用apply 可以将数组传入

var numbers = [3, 2, 44, 11, 66, 7]
var max=Math.max.apply(null,numbers)
console.log(max);

也可以利用 ES6 的 Spread syntax 语法

var max=Math.max(...numbers)

2.2.3 bind方法

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

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

如果只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind

应用场景:不调用函数,但是还想改变this指向

 var o = {name: 'andy'};function fn(a, b) {console.log(this);console.log(a + b);
};
var f = fn.bind(o, 1, 2); //此处的f是bind返回的新函数
f();//调用新函数  this指向的是对象o 参数使用逗号隔开

在这里插入图片描述

解惑

上面的代码

var f = fn.bind(o, 1, 2)

的作用就是将 fn 函数拷贝了一份,名称叫做 f,同时将函数 f 中的 this 修改为对象o,所以函数 f 与 fn 的函数体是一样的,只不过内部 this 的指向不同了

如下代码,才是调用执行函数 f,而上面的代码仅仅完成上面的任务,但不会执行函数 fn 也不会执行新函数 f

f()

应用

页面中有1个div,默认为红色,鼠标悬浮后,变为蓝色,3秒之后恢复成红色

var div = document.querySelector('div')
div.addEventListener('mouseover', function () {this.style.backgroundColor = 'blue'setTimeout(function () {div.style.backgroundColor = 'red'}, 3000);
})

定时器中,this=window,所以不能使用this,必须使用元素名称div

当然可以使用 var that=this 的方式

但这两种方式都有问题,如实现下面的效果

当前页面上有3个div,默认都为红色,鼠标悬浮到某个div上,颜色变为蓝色,3秒后恢复成红色

 var divs = document.querySelectorAll('div')for (var i = 0; i < divs.length; i++) {divs[i].addEventListener('mouseover', function () {// 这里的this=当前触发事件的某个具体divthis.style.backgroundColor = 'blue'setTimeout(function () {// 下面的代码应该怎么写}, 3000);})
}

定时器中的this=window,所以不能使用window

也不能使用divs[i],因为 i 的索引在事件发生时,并不是你想象中的索引

当然可以使用 that=this 的方式,但会多创建局部变量

可以利用bind方法

在这里插入图片描述

这里使用bind最合适,因为仅仅是修改了函数中this的指向,并不会马上执行,3秒之后由系统再次调用

注意:bind 方法会创建一个新函数,所以3秒后调用的是新函数,在新函数中,this=div

问?上面不是需要声明一个变量接收bind创建的新函数吗?为什么这里不需要?

同学,请先搞清楚:什么时候需要返回值,什么时候不需要

2.2.4 call、apply、bind三者的异同

  • 共同点 : 都可以改变this指向

  • 不同点:

    • call 和 apply 会调用函数, 并且改变函数内部this指向.
    • call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递
    • bind 不会调用函数, 可以改变函数内部this指向.
  • 应用场景

    1. call 经常做继承.
    2. apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
    3. bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.

3.严格模式

3.1什么是严格模式

JavaScript 除了提供正常模式外,还提供了严格模式(strict mode)。ES5 的严格模式是采用具有限制性 JavaScript变体的一种方式,即在严格的条件下运行 JS 代码。

严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。

严格模式对正常的 JavaScript 语义做了一些更改:

1.消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。

2.消除代码运行的一些不安全之处,保证代码运行的安全。

3.提高编译器效率,增加运行速度。

4.禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class,enum,export, extends, import, super 不能做变量名

聊聊 TS

3.2开启严格模式

严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。

  • 脚本开启严格模式
  • 函数开启严格模式
 <script>// 在当前脚本中启用严格模式:script开始标记和结束标记之间生效// 'use strict'var name='yhb'age=20function fn(){// 在函数中开启严格模式'use strict'gender='男'}fn()</script>

3.3严格模式中的变化

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode

严格模式对 Javascript 的语法和行为,都做了一些改变。

'use strict'
num = 10 
console.log(num)//严格模式后使用未声明的变量
--------------------------------------------------------------------------------
var num2 = 1;
delete num2;//严格模式不允许删除变量:删除变量的目的是希望释放内存,处理方式一般是将变量的值设置为null
--------------------------------------------------------------------------------
function fn() {console.log(this); // 严格模式下全局作用域中函数中的 this 是 undefined
}
fn();  
--------------------------------------------------------------------------------- function Star() {this.gender = '男';
}
// 不加new 调用Star,则this=undefined
console.log(Star().gender)
// 加new 调用Star,则this=对象
console.log(new Star().gender)
----------------------------------------------------------------------------------
setTimeout(function() {console.log(this); //严格模式下,定时器 this 还是指向 window
}, 2000);  

另外,严格模式下函数的参数名称应该唯一

// 非严格模式下
function f1(m, m) {console.log(m + m)
}
f1(1,2) // 4,分析一下原因为什么事4

严格模式下,会报错

4.高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。

函数作为参数:

在这里插入图片描述

函数作为返回值

在这里插入图片描述

函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。

同理函数也可以作为返回值传递回来

函数作为参数案例:

function fn(m, n, callback) {console.log(m + n)// 函数主体执行结束后才会执行回调函数callback && callback()
}
fn(3, 4, function () {console.log('我就是回调函数')
})

jquery 中大量应用了回调函数,比如动画完成后执行某个操作

5.闭包

5.1变量的作用域复习

变量根据作用域的不同分为两种:全局变量和局部变量。

  1. 函数内部可以使用全局变量。
  2. 函数外部不可以使用局部变量。
  3. 当函数执行完毕,本作用域内的局部变量会销毁。

5.2什么是闭包

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

在JS中,函数每次创建,都会生成闭包(closure),这个闭包包含函数及其词法环境(大概类似于执行上下文)

闭包是一种执行机制:内部函数总是可以访问其所在的外部函数中声明的变量和参数,即使在其外部函数执行结束之后

/*在一个函数中又嵌套函数,在JS中是没有问题的1)内部作用域可以访问外部作用域,所以f2中可以访问f1中的变量2)在全局作用域内,不访问函数f1中的变量*/function f1() {var m = 100return function() {console.log(m)}        }var myfunc=f1()// myfunc 是一个变量,引用了f2的地址,myfunc() 就相当于执行了 f2()myfunc()  // f2()// console.log(m)

通过上面案例发现:闭包可以延伸变量的作用范围。

解惑

一般情况下,下面代码执行后,外部函数f1就执行完毕了,那么函数内部的成员数据都会被销毁,包括变量 m,但是因为内部函数f2使用了变量m,而函数f2又被返回给了变量 myfunc,即变量myfuc引用了内部函数f2,此时

f2还没有执行,所以外部函数f1就不能释放自己的变量m

 f1()

我们可以将 子函数f2 称作闭包函数(有争议)

我们再对闭包做一个总结:函数创建时,形成一个闭包,闭包让内部函数可以访问外部函数的变量和参数,并且通过向外返回内部函数,使得在函数外部也可以访问函数内部的数据

闭包的三个特性

1)函数嵌套函数

2)函数内部可以访问函数外部的变量和参数

3)外部函数执行完毕后,参数和变量不会被垃圾回收机制回收

5.3闭包的案例

  1. 利用闭包的方式得到当前li 的索引号
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量
(function(i) {lis[i].onclick = function() {console.log(i);}})(i);
}	

但是,这种案例使用闭包其实并不是最好的解决方案,因为每次循环都要创建一个函数,而且每个i 的值都会被保存,不能释放,所以执行效率会低

将i的值存储于li标签的自定义属性中的方式更加可取

代码 (任务单)

  1. 闭包应用-3秒钟之后,打印所有li元素的内容

下面代码遍历所有li标签,创建了三个定时器,3秒之后打印每个li标签元素内容

 for (var i = 0; i < lis.length; i++) {(function(i) {setTimeout(function() {console.log(lis[i].innerHTML);}, 3000)})(i);
}

其实,使用我们前面学习的 bind 方法也是可以的

代码 (任务单)

  1. 闭包应用-计算打车价格
/*需求分析
打车起步价13(3公里内),  之后每多一公里增加 5块钱.  用户输入公里数就可以计算打车价格
如果有拥堵情况,总价格多收取2块钱拥堵费*/var car = (function () {var start_price = 13var start_juli = 3var total = 0return {price: function (juli) {if (juli <= start_juli) {total = start_price} else if (juli > 3) {total = start_price + (juli - start_juli) * 5}return total},yondu: function (flag) {return flag ? total + 2 : total}}})()
console.log(car.price(3));
console.log(car.price(10));
console.log(car.yondu(false));
console.log(car.yondu(true));

解惑

1、立即执行函数中的代码立即执行,不会等待调用,所以 变量 car 引用的是一个对象

2、引用的对象中包含两个函数 price 和 yongdu,这两个函数属于立即执行函数的子函数

3、子函数中引用了外部函数中的变量,所以形成了闭包

4、上面的案例并非非要这么编写程序,只是为了练习闭包的使用

6.递归

6.1什么是递归

**递归:**如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数

**注意:**递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。

在这里插入图片描述

6.2利用递归求1~n的阶乘

//利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..nfunction fn(n) {if (n == 1) { //结束条件return 1;}return n * fn(n - 1);}console.log(fn(3));

在这里插入图片描述

6.3利用递归求斐波那契数列

// 利用递归函数求斐波那契数列(兔子序列)  1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
function fb(n) {if (n === 1 || n === 2) {return 1;}return fb(n - 1) + fb(n - 2);
}
console.log(fb(3));

6.4利用递归遍历数据

// 我们想要做输入id号,就可以返回的数据对象var data = [{id: 1,name: '家电',goods: [{id: 11,gname: '冰箱',goods: [{id: 111,gname: '海尔'}, {id: 112,gname: '美的'},]}, {id: 12,gname: '洗衣机'}]}, {id: 2,name: '服饰'
}];
//1.利用 forEach 去遍历里面的每一个对象function getID(json, id) {var o = {};json.forEach(function(item) {// console.log(item); // 2个数组元素if (item.id == id) {// console.log(item);o = item;return o;// 2. 我们想要得里层的数据 11 12 可以利用递归函数// 里面应该有goods这个数组并且数组的长度不为 0 } else if (item.goods && item.goods.length > 0) {o = getID(item.goods, id);}});return o;
}

重点

更多前端知识 诚邀各位前端从事者爱好者加入前端大佬技术交流社区,本社区主要分享技术栈、个人心得、技术交流、问题解惑等前端体系交流

点击下方文字加入

前端大佬技术交流社区

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/248614.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

再见了 React、Angular,Vue3 才是 yyds

切记一定要看到最后&#xff01;&#xff01;&#xff01; 最近看到一篇文章上面是一作者资讯一位IT前辈&#xff0c;问他怎么看待工作 2 年的前端开发&#xff0c;月薪就高达 30k、40k 的现状。 他说&#xff0c;在众多编程技术中&#xff0c;前端算比较容易入门和提升的&am…

系统带你学习 WebAPIs 第一讲

Web APIs 本篇学习目标&#xff1a; 能够通过ID来获取元素 能够通过标签名来获取元素 能够通过class来获取元素 能够通过选择器来获取元素 能够获取body和html元素 能够给元素注册事件 能够修改元素的内容 能够区分innerText和innerHTML的区别 能够修改像div这类普通元素的属性…

react-webpack config webpack@3.4.1

1.最重要的一点 yarn add webpack3.4.1 -g 2. 解决跨域请求 webpack.json 中添加 https://segmentfault.com/q/1010000008190876?_ea1579884 webpack config less -----框架 ----查看考链接 https://blog.csdn.net/mjzhang1993/article/details/79013430转载于:https://w…

系统带你学习 WebAPIs 第二讲

Web APIs 本篇学习目标&#xff1a; 能够说出排他操作的一般实现步骤 能够使用html5中的dataset方式操作自定义属性 能够根据提示完成百度换肤的案例 能够根据提示完成全选案例 能够根据提示完成tab栏切换案例 能够区分元素节点、文本节点、属性节点 能够获取指定元素的父元素 …

Python爬虫学习笔记1:request、selenium、ChromeDrive、GeckoDriver等相关依赖安装

系列学习笔记参考&#xff1a;python3网络爬虫开发实战 requests # pip install requests import requestsselenium Selenium是一个自动化测试工具&#xff0c;利用它我们可以驱动浏览器执行特定的动作&#xff0c;如点击、下拉等 操作 。 对于一些 JavaScript谊染的页面来说&a…

系统带你学习 WebAPIs 第三讲

Web APIs 本篇学习目标&#xff1a; 能够使用removeChild()方法删除节点 能够完成动态生成表格案例 能够使用传统方式和监听方式给元素注册事件 能够说出事件流执行的三个阶段 能够在事件处理函数中获取事件对象 能够使用事件对象取消默认行为 能够使用事件对象阻止事件冒泡 能…

系统带你学习 WebAPIs 第四讲

Web APIs 本篇学习目标&#xff1a; 能够说出常用的3-5个键盘事件 能够知道如何获取当前键盘按下的是哪个键 能够知道浏览器的顶级对象window 能够使用window.onload事件 能够使用window.onresize事件 能够说出两种定时器的区别 能够使用location对象的href属性完成页面之间的跳…

系统带你学习 WebAPIs 第五讲

Web APIs 本篇学习目标: 能够说出常见 offset 系列属性的作用 能够说出常见 client 系列属性的作用 能够说出常见 scroll 系列属性的作用 能够封装简单动画函数 **1.1. **元素偏移量 offset 系列 1.1.1 offset 概述 offset 翻译过来就是偏移量&#xff0c; 我们使用 offset系…

有赞美业微前端的落地总结

2020年4月&#xff0c;有赞美业的前端团队历经7个月时间&#xff0c;完成了美业PC架构从单体SPA到微前端架构的设计、迁移工作。PPT在去年6月份就有了&#xff0c;现在再整理一下形成文章分享给大家。 头图 目录 Part 01 “大话”微前端 微前端是什么 背景 目标 达成价值 …

Markdown 编辑器才是yyds|CSDN编辑器测评

前言 今天小编为大家介绍一款编辑器&#xff0c;也正是小编书写这篇文章所使用的Markdown编辑器&#xff0c;正是广大博友想要发布文章的工具。那么 你知道他的都有哪些方便之处么 下面小编带你了解一下 Markdown是什么 Markdown是一种轻量标记语言,通过简单的语法&#xff…

45天带你玩转Node(第三天)Node环境安装

本篇目标 能够搭建 Node 运行环境掌握 NodeJS 程序的运行方法理解模块化开发理解系统模块和第三方模块理解package.json文件作用 1.Node 开发概述 1.1为什么要学习服务器端开发技术 前端人员为什么要学习服务器端开发技术&#xff1f; 能够和后端程序员更加紧密的配合网站…

系统带你学习 WebAPIs —— 动画篇(第六讲)

Web APIs 本篇学习目标: 能够封装简单动画函数 能够理解缓动动画的封装 能够使用动画函数 能够写出网页轮播图案例 能够写出移动端触屏事件 1.1. 动画函数封装 1.1.1 缓动效果原理 缓动动画就是让元素运动速度有所变化&#xff0c;最常见的是让速度慢慢停下来 思路&#xff…

Git使用教程:最详细、最傻瓜、最浅显、真正手把手教!(转载学习)

一&#xff1a;Git是什么&#xff1f; Git是目前世界上最先进的分布式版本控制系统。 二&#xff1a;SVN与Git的最主要的区别&#xff1f; SVN是集中式版本控制系统&#xff0c;版本库是集中放在中央服务器的&#xff0c;而干活的时候&#xff0c;用的都是自己的电脑&#xff0…

有些话别不当回事

1、别跟堕落的人比堕落。堕落起来非常容易&#xff0c;可是堕落后再想回到不堕落&#xff0c;难。不要给自己颓废的机会。 2、不要以为躲在学校里就可以忽略外面世界的残酷现实。竞争越来越激烈&#xff1a;人越来越多&#xff0c;职位却越来越少。学校里固然温柔&#xff0c;但…

梯度下降更新算法

梯度更新是要同时更新&#xff0c;如下图所示&#xff1a;θ0和θ1同时更新&#xff0c;而不是更新完一个后再更新另一个。 学习率α过小&#xff0c;梯度下降较慢&#xff0c;训练时间增长。若学习率α过大&#xff0c;梯度下降会越过最低点&#xff0c;难以得到最优的结果&am…

《深入理解Spark-核心思想与源码分析》(四)第四章存储体系

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。——《易经》 本章导读 Spark的初始化阶段、任务提交阶段、执行阶段&#xff0c;始终离不开存储体系。 Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈&#xff0c;优先将配置信息、计算结…

Django打造大型企业官网-项目部署

Django打造大型企业官网-项目部署 一、准备工作 1、在开发机上的准备工作 1&#xff09;确认项目没有bug。 2&#xff09;打开终端&#xff0c;进入虚拟环境&#xff0c;再 cd 到项目根目录下&#xff0c;执行命令&#xff1a;pip freeze > requirements.txt&#xff0c;将…

17 | 如何正确地显示随机消息?

我在上一篇文章&#xff0c;为你讲解完order by语句的几种执行模式后&#xff0c;就想到了之前一个做英语学习App的朋友碰到过的一个性能问题。今天这篇文章&#xff0c;我就从这个性能问题说起&#xff0c;和你说说MySQL中的另外一种排序需求&#xff0c;希望能够加深你对MySQ…

QT+VS中使用qDebbug()打印调试信息无法显示

首先右键点击项目名称&#xff0c;找到最后一项属性 然后依次设置为如图所示即可 再次编译后&#xff0c;会弹出CMD窗口&#xff0c;出现qDebug的调试信息。 转载于:https://www.cnblogs.com/WindSun/p/10328404.html

WebAPIs移动端特效——不看你就亏大了

Web APIs 本篇学习目标: ✨能够写出移动端触屏事件 ✨能够写出常见的移动端特效 ✨能够使用移动端开发插件开发移动端特效 ✨能够使用移动端开发框架开发移动端特效 ✨能够写出 sessionStorage 数据的存储以及获取 ✨能够写出 localStorage 数据的存储以及获取 ✨能够说出它们两…