学习目标
- 能够说出函数的多种定义和调用方式
- 能够说出和改变函数内部this的指向
- 能够说出严格模式的特点
- 能够把函数作为参数和返回值传递
1. 函数的定义和调用方式
1.1 函数的定义方式
自定义函数(命名函数)
function fn(){} 复制代码
函数表达式(匿名函数)
var fun = function(){} 复制代码
利用new Function('参数1','参数2','函数体')
该方法比较麻烦,执行效率较低,我们很少使用。
var f = new Function(){'a','b','console.log(a+b)'} f(1,2) 复制代码
结论:
- 所有函数都是Function的实例(对象)
- 函数也属于对象一图明晰函数的原型三角关系
1.2 函数的调用方式
这里要分6种情况
1.普通函数
function fn(){ console.log('走上人生巅峰') } fn() 复制代码
2.对象的方法
var o = { sayHi:function(){ console.log('走上人生巅峰'); } } o.sayHi() 复制代码
3.构造函数
new Star(); 复制代码
4.绑定事件函数
btn.onclick = function(){};// 点击了按钮就可以调用这个函数 复制代码
5.定时器函数
setInterval(function(){},1000)// 这个函数是定时器自动1秒钟调用一次 复制代码
6.立即执行函数
(function(){ console.log('走上人生巅峰') } )()// 立即函数是自动调用 复制代码
2. 函数内this指向问题
这些this的指向,是我们调用函数的时候确定的。调用方式的不同决定了this的指向不同,一般指向我们的调用者。
调用方式 | this指向 |
普通函数调用 | window |
构造函数调用 | 实例对象 原型对象里卖弄的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定调用 | 绑定事件对象 |
定时器调用 | window |
立即执行函数调用 | window |
2.1 call方法及其应用
- call()第一个可以调用函数 第二个可以改变函数内的this指向。
- call()的主要作用可以实现继承
var o = { name:'Bob' } function fn(a ,b){ console.log(this); console.log(a+b); } fn.call(o,1,2) 复制代码
2.2 apply方法及其应用
- 也是调用函数 也可以改变函数内部的this指向
- 但是它的参数必须是数组(伪数组)
- apply主要应用 比如说我们可以利用 apply 借助数学内置对象求最大值,数组不能以前我们如何求数组里的最大值呢?代码如下:
var arr1 = [2, 6, 1, 77, 52, 7]; var maximum = arr1[0]; for ( var i = 1; i < arr1.length; i++) { while (arr1[i] > maximum) { maximum = arr1[i]; } } 复制代码
这种方法看起来好像并不是很复杂,但是现在我们利用apply 借助于数学内置对象求最大值
var arr1 = [2, 6, 1, 77, 52, 7]; var max = Math.max.apply(Math,arr1); 复制代码
代码是不是变得更简洁了,6行代码现在我们只需要1行代码就能解决了(write less,do more 才是我们学习的终止嘛)
2.3 bind方法及其应用
- 不会调用原来的函数 可以改变原来函数内部的this指向
- 返回的是原函数改变this之后产生的原函数拷贝
var o = { name:'Bob' }; function fn(a,b){ console.log(this); console.log(a +b); }; var f = fn.bind(o,1,2); f(); 复制代码
bind方法的应用
应用场景:我们有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向 这个时候就需要用到bind方法了,因为它不会调用原来的函数,在开发中,我们bind方法其实是用的最多的。
举例:我们网站注册页面往往需要有一个按钮点击来发送手机验证码,然后这个按钮通常会被禁用60s。我们暂且改为禁用3s吧!
HTML部分
<input type="text"><button>发送</button> 复制代码
JavaScript部分
方法一
var btn = document.querySelector('button'); btn.onclick = function(){ this.disabled = true;// 按钮被禁用,这里的this指向的是btn这个按钮 // 接下来设置定时器 3s后按钮可用 setTimeout(function(){ btn.disabled = false; },3000) }; // 缺点:定时器里的btn会需要经常修改非常麻烦。 复制代码
方法二
var btn = document.querySelector('button'); btn.onclick = function(){ this.disabled = true;// 按钮被禁用,这里的this指向的是btn这个按钮 // 接下来设置定时器 3s后按钮可用 var that = this; setTimeout(function(){ that.disabled = false; },3000) }; // 缺点:在定时器外面将this赋值给that,这种方法可用,但是会另外开辟内存空间,不太好。 复制代码
方法三(最正确的办法)
var btn = document.querySelector('button'); btn.onclick = function(){ this.disabled = true;// 按钮被禁用,这里的this指向的是btn这个按钮 // 接下来设置定时器 3s后按钮可用 setTimeout(function(){ this.disabled = false; }.bind(this),3000) }; // 此方法很好地解决了前面两种问题,只需要在定时器函数外面改变它的this指向,使它的this指向从window变为btn这个按钮。 复制代码
2.4 call apply bind 总结
相同点:
都可以改变函数内部的this指向。
区别点:
- call 和apply会调用函数,并且改变函数内部this指向
- call和apply传递的参数不一样,call传递参数aru1,aru2形式 apply必须数组形式[arg]
- bind不会调用函数,可以改变函数内部的this指向
主要应用场景:
- call经常做继承。
- apply经常跟数组有关系。比如借助于数学对象实现数组最大值最小值。
- bind 不调用函数,但是还是想改变this指向,比如改变定时器内部的this指向。
3. 严格模式
3.1 什么是严格模式
JavaScript除了提供正常模式外,还提供了严格模式。ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行JS代码。
严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的JavaScript语义做了一些更改:
- 消除了JavaScript语法的一些不合理、不严谨之处,减少了一些怪异行为。
- 消除代码运行的一些不安全之处,保证代码运行的安全。
- 提高编译器效率,增加运行速度。
- 为未来新版本的JavaScript做好铺垫。比如一些保留字:class,super不能做变量名。
3.2 开启严格模式
严格模式可以应用到整个脚本或个别函数中,因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况
1. 为脚本开启严格模式
<script> 'use strict'; // 下面的代码按照严格模式来写 </script> <script> (function(){ 'use strict'; })(); </script> 复制代码
2.为函数开启严格模式
<script> function fn(){ 'use strict'; // 下面的代码按照严格模式来写 } function fun(){ } </script> 复制代码
3.3 严格模式中的变化
严格模式对JavaScript的语法和行为,都做了一些改变。
1. 变量规定
- 在正常模式下,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量必须先用var命令声明,然后再使用。
- 严禁删除已经声明的变量,例如delete x;语法是错误的。
2. 严格模式下this指向问题
- 严格模式下全局作用域中函数中的this是undefined。
- 严格模式下,如果构造函数不加new调用,this是undefined,给它赋值会报错
- new实例化的构造函数指向创建的对象实例
- 定时器this还是指向window
- 事件,对象还是指向调用者
3. 函数变化
- 函数不能有重名的参数
- 函数必须声明在顶层,新版本的JavaScript会引入“块级作用域”(ES6中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。
等等,什么是非函数代码块?答:像if语句中还有for语句里这种区域就属于非函数代码块
"use strict"; if (true) { function f() { } // !!! 语法错误 f(); } for (var i = 0; i < 5; i++) { function f2() { } // !!! 语法错误 f2(); } function baz() { // 合法 function eit() { } // 同样合法 } 复制代码
4.高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
<script> // 函数作为参数 function fn(callback){ callback&&callback(); } fn(function(){alert('hi')} // 函数作为返回值输出 function fn(){ return function(){} } fn(); </script> 复制代码
此时fn就是一个高阶函数
函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。
作者:Cvgod
链接:https://juejin.cn/post/6955436283548303397
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。