apply和call的区别:
区别不大,只在参数。apply是something.apply(obj, arguments),第一个obj是一个对象,然后arguments是一个数组。call是(obj, arg1, arg2, …..)。第一个参数是一个对象,剩下的是函数所需的其它参数。如果执行a.add()apply(b),那么将执行一遍add函数,而且在add函数里,所有的this都将指向b。
两个函数均可起到对象冒充的作用。举个apply的例子
1 | function Person(name, age) { //创建一个Person类 |
上文的Person.apply()中,apply接受了两个参数,一个是this(即Student),这样一来,Person函数中的所有this将被替换成Student并执行一遍。最后出来的效果就是Student.name有了值,Student.age和Student.getInfo也都有了内容。如此一个典型的类继承就这么完成了(当然JavaScript的优势并不是这种继承类型)。
好了apply和call除了这个有没有稍微振奋人心的作用?有的,比如下次面试的时候面试官问到你,如何拼接两个数组的时候,这个就派上用场了。具体的我们先声明两个数组1
2var a = [1, 2, 3];
var b = [4, 5, 6];
然后使用Array.prototype.push().apply(a, b);(由于参数问题这里的call就不适用啦)。这里b将被逐个push进a里,最后完成拼接。当然你可能会说,直接使用concat函数不就可以了吗?
匿名函数和闭包
匿名函数
函数对于写过代码的大家来说怕是熟得不能再熟了。来个普普通通的函数先:1
2
3function add(a, b) {
return a + b;
}
这函数是普通了点,它的功能也无非是返回两个数的和。
那匿名函数呢,又是什么?
其实说白了就是没有名字的函数,像这个样子:1
2
3function () {
console.log("8080");
}
这写法有点新奇~,估计不少同学没试过这样来写函数吧,是的,它除了报错之外不会带来其他效果。首先它不能运行,就算能运行它也不能被调用,因为没有名字。
那有什么办法让它起作用呢?很简单,给它加个括号就行:1
2
3(function () {
console.log("8080");
})
加上括号之后,你会发现,报错不见了。嗯目前它已经是一个匿名函数的样子了,只要你在最后那里加上(),它立马就能运行起来:1
2
3(function () {
consol.log(8080);
})()
既然知道怎么用匿名函数了,接下来就探讨一下它和普通函数的区别吧。
我们知道,有没有名字对于程序来说有着重大意义。如果写一个无名函数,我们不用发愁怎么给它起名字,更不用担心会不会出现命名冲突了。还顺道解决了全局污染的问题。
哇~,听起来匿名函数是个好东西。嗯的确是这样,写代码的日常中,我们可以用匿名函数来运行一段小程序,还能用它来分割代码块,实现局部代码各不干扰的效果:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// ---代码块 1
(function () {
var a = 1,
b = 2;
// ...do something
console.log('start');
})(); // 小心不要漏写了这里的分号";"
// ---代码块 2
(function () {
var a = 1, //不会与之前的变量冲突
b = 2;
// ...do something
console.log('during');
})();
// ---代码块 3
(function () {
var a = 1, //不会与之前的变量冲突
b = 2;
// ...do something
console.log('ending');
})();
匿名函数还有更多新奇的作用,这里就不一一详述了~
闭包
接下来聊聊闭包,上网搜下闭包的定义,官方给的也是有点生动,我记录如下:1
所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分
不知道大家看不看的懂,反正我是一脸懵13,参考一些浅显的说法时,它们会这样理解闭包:“所谓闭包:就是函数的函数”。闭包的精髓在于作用域,JavaScript一个有趣的地方在于,它创建的每个function都会产生一个作用域。有这么个东西存在我们就可以这么玩:1
2
3
4
5
6function func () {
var a = "2333";
return function () {
return a;
}
}
值得注意的是,最后这个函数被外面的函数返回出去了,这样才能算是合格的闭包。
通过闭包,我们足以把局部变量驻留在内存中(避免了全局变量),举个例子,我们想实现变量的累加,一个简单的做法是把变量做成全局的,
这样大家都能访问到,累加就不是难事:1
2
3
4
5
6
7
8
9
10var count = 10; //count是全局变量
function plus() { // 累加函数
count ++;
}
plus(); //调用累加->11
console.log(count);
plus(); //再累加 ->12
console.log(count);
// ...
但全局变量是个危险品,换成局部变量又实现不了累加1
2
3
4
5
6
7
8
9
10
11function plus() { // 累加函数
var count = 10; //count是局部变量
count ++;
return count;
}
plus(); //累加 ->11
console.log(count);
plus(); //累加失败 还是 ->11
console.log(count);
// ...
遇到这种情况,就是闭包的发挥用处的时候啦:1
2
3
4
5
6
7function plus () { // plus是个闭包
var count = 10;
return function () {
count++;
return count;
}
}
不过调用的时候要小心,如果是直接向下面这样调用的话是起不到效果的,因为变量被不断地初始化了:1
2
3
4
5
6
7
8
9
10
11
12function plus () { // plus是个闭包
var count = 10;
return function () {
count++;
return count;
}
}
// ----错误的调用方式---
console.log(plus()()); //初始化count并累加
console.log(plus()()); //再度初始化count并累加
console.log(plus()()); // ...
console.log(plus()()); // ...
应该先把plus函数用临时变量存起来1
2
3
4
5
6// ----正确的调用方式---
var closure = plus ();
console.log(plus()());
console.log(plus()());
console.log(plus()());
console.log(plus()());
这样的话就实现累加了。
PS(这也是我后来才知道的真相):由于闭包里作用域返回的局部变量资源不会立即被销毁回收,所以会占用内存,过多使用会导致性能下降,建议在有必要的时候才使用它~。