函数进阶
# 1.万物皆对象
JavaScript中基本所有都是对象,函数也是一个对象
函数的定义
//1.正常创建
function fn(){}
//2.匿名表达式 + 定义新变量
var fn = function(){}
//3.形参写法:new Function('参数1','参数2','函数体')
var fn = new Function('a','b','console.log(a+b)');
fn(1,2);
1
2
3
4
5
6
7
2
3
4
5
6
7
六种调用方式
//1.普通函数
function fn(){console.log('人生的巅峰')};
fn();
//2.对象调用
var obj = {
sayHi:function(){console.log('hello world!')};
}
obj.sayHi();
//3.构造函数
function Star(){};
new Star();
//4.绑定事件函数
btn.onclick = function(){};
//5.定时器函数
setInterval(function(){},1000);
//6.立即执行函数,()()
(function(){})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2. 函数中this的默认指向
调用方式 | this指向 |
---|---|
function(){} | window |
new 构造函数 | 取决于实例对象 |
对象调用obj.sayHi() | 取决于obj |
setInterval(function(){},1000) | window |
(function(){})() | window |
# 3.改变this指向
# call()
功能:直接调用,改变this的指向
fn.call(this,arg1,arg2,...);
1
# apply()
功能:使用同call
,区别在于输入的必须是数组或伪数组
fn.apply(this,[arg1,arg2,...]);
// 应用场景,可以帮助取出[]
// 如:Math.max只能接受数字型1,2,3,而通过apply可以很简单的将数组取出,类似ES6中的扩展符...
array = [1 ,2 ,55, 99];
let maxNum1 = Math.max.apply(Math,array);//第一个参数是指针对象,Math是调用max函数的这个对象
let maxNum2 = Math.max(...array);
1
2
3
4
5
6
7
2
3
4
5
6
7
# bind()
功能:调整this
的指向,但是不立即执行,非常常用的函数
⚠️注:返回由指定的this值和初始化参数改造的原函数的拷贝;
let o = {
name: 'andy'
}
function fn(a,b){
console.log(a + b);
}
let f = fn.bind(o,1,2); // 绑定时不执行
f(); // 后执行
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# call apply bind总结
call
经常做继承apply
经常跟数组有关系,比如借助数学对象实现最大值和最小值bind
不调用函数,但是还想改变this
指向,比如改变定时器内部this
指向
# 4.高阶函数
高阶函数是对其他函数进行操作的函数,分两类,将函数进行输入或将函数进行输出。
- 函数作为输入[典型:回调函数]
// 方式一:将函数进行输入,最典型的就是作为回调函数
function fn(a,b,callback){
console.log(a+b);
callback && callback(); // 很巧,如果有,才去执行callback
}
// 经典回调函数
$("div").animate({left: 500},function(){
$("div").css("backgroundColor","purple");
});
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- 函数作为输出[
return function(){}
] [典型:闭包]
// 方式二:return function
function fn(){
return function(){};
};
fn();
1
2
3
4
5
2
3
4
5
# 5.闭包
变量作用域分两类:
- 全局变量:函数内部可以。
- 局部变量:函数外部不可使用,且执行完毕后,局部变量会销毁。
闭包(closure):是指有权访问另一个函数作用中变量的函数
闭包的简单实现:
// 最简单的闭包:[内访外]内部函数可以访问外部函数的变量
function fn1(){
var num = 10;
function fn2(){
console.log(num);// 10
}
fn2();
}
fn1();
// [外访内]fn1函数外部访问fn1中的变量
function fn1(){
var num = 10;
return function(){
console.log(num);
}
}
var f = fn();
f();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
⚠️注意:难点主要在外访内
外访内时,第一步
var f = fn()
是取出return
中的内部函数内部函数需要调用外部函数中的变量
num
,所以如果f()
没有调用,则局部变量暂时不会销毁,此时我们称fn1
为闭包函数。
点击查看
闭包的三个案例
- 循环点击事件
<ul class="nav">
<li>榴莲</li>
<li>臭豆腐</li>
<li>大猪蹄子</li>
<li>鲱鱼罐头</li>
</ul>
<script>
var lis = document.querySelector('.nav').querySelectorAll('li');
// 1. 循环绑定onclick
for(let i=0;i<lis.length;i++){
lis[i].index = i;
lis[i].onclick = function(){
console.log(this.index);
}
}
// 2. 利用闭包的方式得到索引号
for(let i=0;i<lis.length;i++){
// 外部函数:立即执行函数
// 内部函数:绑定在button按钮上,立即函数
// 外部变量: i索引
(function(i){
lis[i].onclick = function(){
console.log(i);
}
})(i)
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
⚠️注意:闭包不代表性能高
- 处理异步函数(设置为内部函数)
// 试图发现下面错误的地方:
let lis = document.querySelector('.nav').querySelectorAll('li');
for (let i = 0;i<lis.lenght;i++){
setTimeout(function(){
console.log(lis[i].innerHTML);
},3000);
}
// setTimeout属于异步程序,会压入异步执行队列
// 异步程序是当同步队列执行结束后,才执行,故上述程序会报错的。
// 改进: 在原先的异步函数的基础上又外包了一个闭包函数
let lis = document.querySelector('.nav').querySelectorAll('li');
for (let i = 0;i<lis.lenght;i++){
(function(i){
setTimeout(function(){
console.log(lis[i].innerHTML);
}, 3000)
})(i);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
这里的闭包函数:
(function(i){
//异步函数执行代码,可以直接读到外部的变量i
function(){}
})(i)
1
2
3
4
2
3
4
- 计算打车价格
let car = (function(){
let start = 13;// 起步价
let total = 0;// 总价
return {
// 正产的总价
price: function(n){
if(n<=3){
total = start;
}else {
total = start + (n-3)*5;
}
return total;
},
// 拥堵之后的费用
yd: function(flag){
flag ? total + 10 : total;
}
}
})();
console.log(car.price(5));
console.log(car.yd(true));
console.log(car.price(1));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
这又是一个比较复杂的
return
的高阶函数,内部函数由对象object
输出出来,外部由object
调用这个函数。
思考题:
// 思考题1:
let name = "The Window";
let object = {
name : : "My Object",
getNameFunc: function(){
retrurn function(){
return this.name;
}
}
}
console.log(object.getNameFunc()());// 全局变量下的变量"The Window"
// 思考题2:
let name = "The Window";
let object = {
name : : "My Object",
getNameFunc: function(){
let that = this;
retrurn function(){
return that.name;
}
}
}
console.log(object.getNameFunc()());// 对象内部的变量"My Object"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
解析:object.getNameFunc()()
的过程
第一步需要将变量中的函数保存取出:
let f = object.getNameFunc()
相当于函数赋值:
f = function(){}
,此时this
的指向为f
的内存地址window
第二步运行真实函数:
f()
解析2:
- 当
f()
运行时,先将this
的指向缓存下来,再执行that.name
就可以找到对象的属性了。
# 6.浅拷贝与深拷贝
浅拷贝
// 传统的浅拷贝:由遍历对象实现 let obj = { id: 1, name: 'andy', msg: {age: 18}, fn: function() { console.log('a'); } }; var o = {}; for (let k in obj){ // k是属性名,obj[k]属性值 o[k] = obj[k]; } console.log(o);//拷贝后的对象 o.msg.age = 20; console.log(obj);//发现原对象被修改了 // 内置写法: 浅拷贝的简单写法 Object.assign(o, obj)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21使用迭代函数方式实现深拷贝
function deepClone(obj){ let objClone = Array.isArray(obj) ? [] : {}; if (obj && typeof obj === 'object') { for(let key in obj){ if (obj[key] && typeof obj[key] === 'object'){ objClone[key] = deepClone(obj[key]); }else{ objClone[key] = obj[key] } } } return objClone; }
1
2
3
4
5
6
7
8
9
10
11
12
13
编辑 (opens new window)
上次更新: 2022/04/06, 15:04:00