函数进阶
函数的定义和调用
函数的定义方式
函数声明方式function关键词(命名函数)
function fn(){}
函数表达式(匿名函数)
var fun = function(){}
new Function
- Function里面参数都必须是字符串格式
new Function('参数1','参数2','函数体')
var f = new Function()
函数的调用方式
普通函数
function fn(){
console.log('函数调用')
}
//方式一
fn()
//方式二
fn.call()
对象的方式
var obj = {
working: function() {
console.log('函数调用')
}
}
obj.working()
构造函数
function GetNum(){}
new GetNum()
绑定事件函数
//点击事件
btn.onclick = function(){}
定时器函数
setInterval(function(){},1000)
立即执行函数
(function(){
console.log('自动调用')
})()
this
- this的指向,是当我们调用函数的时候确定的,调用方式的不同决定了this的指向不同,一般指向调用者
调用方式 | this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象 原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
改变函数内部this指向
- JS为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部this的指向问题,常用的bind()、call()、apply()三种方法
call()方法
- call()方法调用一个对象,简单理解为调用函数的方式,但是它可以改变函数的this指向
fun.call(thisArg,arg1,arg2,arg3,...)
var obj = {
uname: '张三'
}
function fn(){
console.log(this)
}
//fn.call()//window
fn.call(obj)//obj
apply()方法
-
apply()方法调用一个函数,调用函数的方式,但是它可以改变函数的this指向
fun.apply(thisArg,[argsArray])
- thisArg: fun函数运行时指向的this值
- argsArray: 传递的值,必须包含在数组里面
- 返回值就是函数的返回值,因为它就是调用函数
var obj = { uname: '张三' } function fn(){ console.log(this) } //fn.call()//window fn.apply(obj)//obj
- apply的主要应用
//求最大值 var arr = [12,13,89,65,46] let maxNum = Math.max.apply(Math,arr) console.log(maxNum)
bind()方法
-
bind()方法不会调用函数,但是能改变函数内部this指向
fun.bind(thisArg,arg1,arg2,arg3,...)
- thisArg: fun函数运行时指定this的值
- arg1,arg2: 传递的其他参数
- 返回由指定的this值和初始化函数改造的原函数拷贝
<script> var obj = { uname: '张三' } function fun(){ console.log(this) } fun()//window let func = fun.bind(obj) func()//obj </script>
bind()常见应用
- 函数不需要立即调用,但是又想改变这个函数内部的this指向,此时可以调用bind
<button style="background-color: aqua;">点击
</button>
<script>
var btn = document.querySelector('button')
btn.onclick = function (){
this.disabled = true
setInterval(function(){
this.disabled = false
}.bind(this),3000)
}
</script>
严格模式
- JS除了提供正常模式外,还提供了严格模式,ES5的严格模式是采用具有限制性JS变体的一种方式,即在严格的条件下运行JS代码
- 消除了JS语法的一些不合理、不严谨之外,减少了一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 提高编译器效率,增加运行速度
- 禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版的JS做好铺垫
开启严格模式
- 严格模式可以应用到整个脚本或个别函数中,可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况
为脚本开启严格模式
- 为整个脚本开启严格模式,需要在所有语句之前放一个特定语句"use strict"或’use strict’
<script>
'use strict';
</script>
为函数开启严格模式
- 给某个函数开启严格模式,需要把’use strict’或"use strict"声明放在函数体所有语句之前
function fn(){
'use strct';
}
严格模式中的变化
变量规定
- 正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var/let/const声明,然后再使用
- 严禁删除已经声明的变量
var num = 10
//编译通不过
delete num //'delete' cannot be called on an identifier in strict mode.
严格模式下this指向问题
- 以前在全局作用域函数中的this指向window对象
- 严格模式下全局作用域中函数中的this指向是undefined
<script>
'use strcit';
function fn(){
console.log(this)
}
fn()//undefined
</script>
- 严格模式下,如果构造函数不加new调用,this会报错
<script>
'use strict'
var num = 10
function Person(name,age){
this.name = name
this.age = age
}
/*正常模式下可以执行
Person('张三',20)
console.log(window.name)//张三
*/
var person = new Person('张三',20)
console.log(person.name)//张三
</script>
- 严格模式下,定时器this还是指向window
- 事件、对象还是指向调用者
函数变化
- 不能有重名参数
- 函数必须声明在顶层,新版本JS会引入"块级作用域"(ES6中已存在),为了与新版本接轨,不允许在非函数的代码内声明函数
<script>
'use strict'
//此处能声明 不知道为什么【未解决问题】
for(let i = 0; i < 10; i++){
function fn(){
console.log('严格模式下,函数声明在顶层')
} //Identifier expected
fn()
}
function baz(){
function eit(){
}
}
</script>
高阶函数
- 高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
函数作为参数
<script>
function fn(num1,num2,callback){
console.log(num1 + num2)
callback && callback()
}
fn(2,2,function(){
console.log('函数作为参数传递');
})
</script>
函数作为返回值输出
<script>
function fn(){
console.log('函数作为返回值传递')
return function (){
console.log('函数作为返回值传递+1')
}
}
fn()()
</script>
闭包
- 闭包是指有权访问另一个函数作用域中变量的函数
- 闭包的作用: 延伸了变量的作用范围
变量作用域
- 变量根据作用的不同分为两种
- 全局变量和局部变量
- 函数内部可以使用全局变量
- 函数外部不可以使用局部变量
- 当函数执行完毕,本作用域内的局部变量会销毁
<script>
//闭包: func这个函数作用域访问了另一个函数fun里面的局部变量 number
function fun(){
var number = 100
function func(){
console.log(number)
}
func()
}
fun()
</script>
应用案例
<ul class="nav">
<li>数组</li>
<li>高数</li>
<li>函数</li>
<li>高阶函数</li>
<li>微积分</li>
</ul>
<script>
/*点击任意一个显示出它的下标*/
let lis = document.querySelector('.nav').querySelectorAll('li')
for(var i = 0; i < lis.length; i++){
lis[i].index = i
lis[i].onclick = function(){
console.log(i)//5
console.log(this.index);//根据点击li显示下标
}
}
//利用闭包实现上面功能
for (var j = 0; j < lis.length; j++){
(function (j){
lis[j].onclick = function(){
console.log(j)//根据点击li显示下标
}
}
)(j)
}
/*3s之后,打印所有li元素的内容*/
for(var k = 0; k < lis.length; k++){
(function(k) {
setTimeout(function(){
console.log(lis[k].innerHTML)
},3000)
})(k)
}
</script>
递归
- 一个函数在内部可以调用其本身,那么这个函数就是递归函数
<script>
//递归函数: 函数内部自己调用自己,这个函数就是递归函数
function fn(num){
if(num == 1){
return 1
}
return num * fn(num -1)
}
console.log(fn(10))
</script>
-----------------------------------------------------------------
<script>
//利用递归函数求斐波那契数列 1 1 2 3 5 8 13 21
//求出输入一个数字n,就可以求出这个数字对应的序列值
function func(n){
if(n == 1 || n == 2){
return 1
}else{
return func(n-1)+func(n-2)
}
}
console.log(func(8))
</script>
递归案例
- 利用递归遍历数据
<script>
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
name: '冰箱'
},{
id: 12,
name: '洗衣机'
},{
id: 13,
name: '电视机'
}]
},{
id: 2,
name: '服饰'
},{
id: 3,
name: '数码产品',
goods: [{
id: 31,
name: '智能手机'
}]
}]
//输入id号,就可以返回数据对象
function func(json,id){
json.forEach(function(item){
if(item.id == id){
console.log(item)
}else if(item.goods && item.goods.length > 0){
func(item.goods,id)
}
})
}
func(data,1)
func(data,31)
</script>