函数进阶
1. 函数的定义和调用
函数定义的方式
- 函数声明方式function关键字(命名函数)
function fn() {}; - 函数表达式 匿名函数
var fun = function() {}; - 利用 new Function(‘参数1’,‘参数2’, … ,‘函数体’);
var f = new Function(‘a’, ‘b’, ‘console.log(a + b)’);
注意: 所有函数都是Function的实例,函数属于对象
函数的调用方式
- 普通函数:fn(); fn.call()
- 对象的方法:obj.fn()
- 构造函数: new
- 绑定事件函数: 触发相应事件
- 定时器函数: 时间到了就执行
- 立即执行函数:自动调用
2. 函数中this指向问题
一般情况下this指向调用者
- 普通函数:window
- 对象的方法:该方法所属对象
- 构造函数: 实例对象
- 绑定事件函数: 绑定事件对象
- 定时器函数: window
- 立即执行函数:window
改变函数内的this指向
- call() fun.call(要指向的对象, arg1, arg2…)
调用函数并改变this指向,主要用于实现继承 - apply(要指向的对象, [argsArray]) 也可以调用函数
argsArray:传递的值,必须包含在数组里
主要应用:借助于数学内置对象求值
Math.max.apply(Math, arr) - bind 不会调用函数
返回值是指定的this值和初始化参数改造的原函数拷贝
// 1. 不会调用原来的函数 可以改变原来函数内部的this 指向
// 2. 返回的是原函数改变this之后产生的新函数
// 3. 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind
// 4. 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮
var btns = document.querySelectorAll('button');
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
this.disabled = true;
setTimeout(function() {
this.disabled = false;
}.bind(this), 2000);
}
}
3. 严格模式
- 消除了JavaScript语法的一些不合理、不严谨之处,减少了一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 提高编译器效率,增加运行速度
- 禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的JavaScript做铺垫
开启严格模式: 可以应用到整个脚本或个别函数中
- 为脚本开启严格模式
<!-- 为整个脚本(script标签)开启严格模式 -->
<script>
'use strict';
// 下面的js 代码就会按照严格模式执行代码
</script>
<script>
(function() {
'use strict';
})();
</script>
- 为某个函数开启严格模式
function fn() {
'use strict';
// 下面的代码按照严格模式执行
}
function fun() {
// 里面的还是按照普通模式执行
}
严格模式中的变化:
- 变量规定
①变量必须先声明,再使用
②不能随意删除已经声明好的变量 - this指向
①以前全局作用域下的函数中,this指向的是window;严格模式全局作用域中函数中的this指向的是undefined
②以前构造函数不加new也可以当作普通函数调用,this指向window;严格模式下,如果构造函数不加new调用, this指向的是undefined,如果给它赋值则会报错 - 函数变化
①严格模式下函数里面的参数不允许有重名
②不允许再非函数代码块里创建函数,比如for循环里,if里
4. 高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或者将函数作为返回值输出
function fn(a, b, callback) {
console.log(a + b);
callback && callback();
}
fn(1, 2, function() {
console.log('我是最后调用的');
});
闭包
变量的分类: 全局变量、局部变量
1.函数内部可以使用全局变量
2.函数外部不可以使用局部变量
3.当函数执行完毕,本作用域内的局部变量会销毁
闭包: 有权访问另一个函数作用域中变量的函数
function fn() {
var num = 10;
function fun() {
console.log(num);
}
fun();
}
fn();
被访问的变量所在的函数就是一个闭包函数
闭包的主要作用: 延伸了变量的作用范围
// 我们fn 外面的作用域可以访问fn 内部的局部变量
function fn() {
var num = 10;
// function fun() {
// console.log(num);
// }
// return fun;
return function() {
console.log(num);
}
}
var f = fn();
f();
// 类似于
// var f = function() {
// console.log(num);
// }
// var f = function fun() {
// console.log(num);
// }
经典闭包面试题:
利用闭包的方式得到所有li的索引号
for (var i = 0; i < lis.length; i++) {
// 利用for循环创建了4个立即执行函数
// 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量
(function(i) {
// console.log(i);
lis[i].onclick = function() {
console.log(i);
}
})(i);
}
5. 递归
一个函数在内部可以调用其自身,这个函数就是递归函数
防止栈溢出一定要加推出条件return
- 原生js里的浅拷贝
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
}
};
var o = {};
for (var k in obj) {
// k 是属性名 obj[k] 属性值
o[k] = obj[k];
}
Object.assign(o, obj);//ES6
- 原生js里的深拷贝
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
};
var o = {};
// 封装函数
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
// 判断我们的属性值属于那种数据类型
// 1. 获取属性值 oldobj[k]
var item = oldobj[k];
// 2. 判断这个值是否是数组
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item)
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象
newobj[k] = {};
deepCopy(newobj[k], item)
} else {
// 4. 属于简单数据类型
newobj[k] = item;
}
}
}
deepCopy(o, obj);