共计 4119 个字符,预计需要花费 11 分钟才能阅读完成。
函数的定义和调用
函数的定义方式
1.函数声明方式 function关键字(命名函数)
function fn() {}
2.函数表达式(匿名函数)
let fn = function() {}
3. new Function()
里面的参数都必须是字符串格式
let fn = new Function('参数1', '参数2', '函数体')
let fn = new Function('x', 'y', 'console.log(x + y)')
函数的调用方式
1.普通函数
function fn() {
console.log('被调用了')
}
fn()
fn.call()
2.对象的方法
let obj ={
function fn() {
say: function() {
console.log('被调用了')
}
}
}
obj.say()
3.构造函数
Function Star() {}
new Star()
4.绑定事件函数
btn.onclick = function() {
}
btn.addEventListener('click', function() {
})
// 点击了就可以调用这个函数
5.定时器函数
setInterval(function () { }, 1000)
// 这个函数是定时器自动1秒钟调用一次
6.立即执行函数
(function () {
console.log('立即执行');
}())
(function () {
console.log('立即执行');
})()
// 立即执行函数是自动调用
this
这些 this
的指向,是当我们调用函数的时候确定的;调用方式的不同决定了 this
的指向不同
调用方式 | this 指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象,原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定调用 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
改变函数内部 this
指向
JS为我们专门提供了一些函数方法来帮助我们处理函数内部的 thsi
指向问题,常用的有 bind()
、 call()
、 apply()
三种方法
call()
方法
call()
方法调用一个对象;简单理解为调用函数的方式,但是它可以改变函数的 this
指向
fn.call(thisArg, arg1, arg2, ...)
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)
}
let son = new Son('刘德华', 18, '男')
thisArg
:当前调用函数this
的指向对象agr1
,arg2
:传递的其他参数
apply()
方法
apply()
方法调用一个对象;简单理解为调用函数的方式,但是它可以改变函数的 this
指向
fn.apply(thisArg, [argsArray])
let arr = [1, 2, 3, 4, 5]
let max = Math.max.apply(Math, arr)
thisArg
:在fn函数运行时指定的this
值argsArray
:传递的值,必须包含在数组里面- 返回值就是函数的返回值,因为它就是调用的函数
bind()
方法
bind()
方法不会调用函数;但是能改变函数内部 this
指向
let f = fn.bind(thisArg, arg1, arg2, ...)
f()
btn.addEventListener('click', function() {
this.disabled = true // 此时this指向的是btn这个按钮
// let that = this
setTimeout(function() {
// that.disabled = false // 定时器函数里面的this指向的是window
this.disabled = false // 此时定时器函数里面的this指向的是btn
}.bind(this),3000) // 这个this指向的是btn这个对象
})
thisArg
:在fn函数运行时指定的this
值agr1
,arg2
:传递的其他参数- 返回由指定的
this
值和初始化参数改造的原函数拷贝
call()
apply()
bind()
总结
相同点:
都可以改变函数内部 this
指向
区别点:
call()
和apply()
会调用函数,并且改变函数内部this
指向call()
和apply()
传递的参数不一样,call()
传递参数 aru1, aru2…形式apply()
必须是数组形式[arg]
bind()
不会调用函数,可以改变函数内部this
指向
主要应用场景:
call()
经常做继承apply()
经常和数组有关系,比如借助于数学对象实现数组最大值最小值bind()
不用调用函数,但是还想改变this
指向,比如改变定时器内部的this
指向
严格模式
什么是严格模式
- JS除了提供正常模式外,还提供了严格模式(strict mode)
- ES5的严格模式是采用具有限制性JS变体的一种方式,即在严格模式的条件下运行JS代码
- 严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略
严格模式对正常的JS语义做了一些更改:
- 消除了JS语法的一些不合理、不严谨之处、减少了一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 提高编译器效率,增加运行速度
- 禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的JS做好铺垫;比如一些保留字:
class
、enum
、export
、extends
、import
、super
不能做变量名
开启严格模式
严格模式可以应用到整个脚本或个别函数中;因此我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况
为脚本开启严格模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句 "use strict"
或'use strict'
<script>
"use strict";
</script>
<script>
(function() {
"use strict";
// 为立即执行函数开启严格模式
}())
</script>
为函数开启严格模式
function() {
"use strict";
}
严格模式中的变化
严格模式变量规定
- 在正常模式中,如果严格变量没有声明就赋值,默认是全局变量;严格模式禁止这种用法,变量都必须先声明,然后在使用
<script>
'use strict'
num = 10
consoel.log(num) // 报错,必须先声明在使用
</script>
- 严禁删除已经声明的变量;例如,
delete x
; 语法是错误的
<script>
'use strict'
let num = 10
delete num // 报错,严禁删除已经声明的变量
</script>
严格模式 this
指向问题
- 以前在全局作用域中函数中的
this
指向window
对象,严格模式下全局作用域函数中的this
指向的是undefined
<script>
'use strict'
function fn() {
console.log(this) // 严格模式指向undefined
}
fn()
</script>
- 以前构造函数时不加
new
也可以调用,当普通函数,this
指向全局对象严格模式下不加new
调用this
指向的是undefined
如果给它赋值会报错 new
实例化的构造函数指向创建的对象实例- 严格模式下定时器的
this
还是指向window
- 严格模式下的事件、对象还是指向调用者
严格模式函数变化
- 函数不能有重名的参数
- 函数必须声明在顶层,新版本的JS会引入”块级作用域”(ES6中已引入);为了与新版本接轨,不允许在非函数的代码块内声明函数
高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
function fn(callback) {
callback && callback()
}
fn(function () {
console.log('Hi');
})
function fn() {
return function () { console.log('Hi'); }
}
fn()
此时fn就是一个高阶函数
函数也是一种数据类型,同意作为参数,传递给另一个参数使用;最典型的就是回调函数
闭包
变量作用域
变量根据作用域的不同分为两种:全局变量和局部变量
- 函数内部可以使用全局变量
- 函数外部不可以使用局部变量
- 当函数执行完毕,本作用域内的局部变量会销毁
什么是闭包
闭包(closure)指有权访问另一个函数作用域中变量的函数
简单理解就是,一个作用域可以访问另一个函数内部的局部变量
闭包的主要作用:延伸了变量的作用范围
闭包案例
循环注册点击事件
var btns = document.querySelectorAll('button')
// 利用闭包的方式得到当前按钮的索引号
for (var i = 0; i < btns.length; i++) {
// 利用for循环创建了4个立即执行函数
(function (i) {
btns[i].addEventListener('click', function () {
console.log(i);
})
}(i))
}
循环中的 setTimeout()
var btns = document.querySelectorAll('button')
// 闭包应用 3秒之后打印所有btn元素的内容
for (var i = 0; i < btns.length; i++) {
(function (i) {
setTimeout(function () {
console.log(btns[i].innerHTML);
}, 3000)
}(i))
}
闭包总结
闭包是什么
闭包是一个函数(一个作用域可以访问另一个函数的局部变量)
闭包的作用是什么
延伸变量的作用范围
递归
什么是递归
- 如果一个函数内部可以调用其本身,那么这个函数就是递归函数
- 简单理解:函数内部自己调用自己,这个函数就是递归函数
- 递归函数的作用和循环效果一样
- 由于递归很容易发生”栈溢出“错误(stack overflow),所以必须要加退出条件
return
let num = 1
function fn() {
console.log('打印6次')
if (num === 6) {
return // 递归里面必须加退出条件
}
num++
fn()
}
fn()