【Q】网易微专业-JS16 函数进阶

Q1:(function(){}()最后这个()有什么意义?必须有这个()才是闭包??

var sum = (function(){
            var add=function(i,j){
                return i+j;
            }
            return function(i,j){
                add(i,j);
            }
        })()//(function(){}()最后这个()有什么意义?必须有这个()才是闭包??)```

1.函数定义
注:函数实例化很少用
![Paste_Image.png](http://upload-images.jianshu.io/upload_images/316258-e767e47bbb2978f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

Q1:函数声明在函数定义前能被调用,为什么?
Q2:函数表达式和实例化在函数定义前不能被调用,为什么?

2.代码执行过程
▶代码执行前先预解析(将变量、变量声明、函数定义 提前处理),然后单步执行JS代码。
▶函数声明被前置到顶部执行。
▶当用函数声明重复定义一个函数时,只有最后一次定义有效。
![](http://upload-images.jianshu.io/upload_images/316258-1e45e15dff2f2a4f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![](http://upload-images.jianshu.io/upload_images/316258-a683a60084e30007.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![思考题](http://upload-images.jianshu.io/upload_images/316258-24271750ef0f9e5c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>A:执行结果:
函数声明:101
函数表达式:11
函数表达式:11
——

var add1; //最顶部的变量声明
function add1(i) {
console.log("函数声明:"+(i+1));
}
function add1(i) {
console.log("函数声明:"+(i+100));
}
add1(1); //当执行这句时,上面两个相同的函数声明,后面的覆盖了前面的声明,因此按照第二个函数的结果输出 为101
add1 = function (i) { //代码所在位置的变量赋值
console.log("函数表达式:"+(i+10));
};
add1(1); //此时的add1指向了最后定义的匿名函数了,因此执行结果为 11
add1(1); //没有其他代码改变add1的指向,因此add1(1)的执行结果还是11```

3.函数定义之间的区别


【Q】网易微专业-JS16 函数进阶_第1张图片
Paste_Image.png
【Q】网易微专业-JS16 函数进阶_第2张图片
Paste_Image.png

4.函数调用4种模式
注:自定义构造函数建议首字母大写,便于相互理解


【Q】网易微专业-JS16 函数进阶_第3张图片
函数调用模式

⑴方法调用模式

 var myNumber = {
   value: 1,
   add: function(i){
    console.log(this);
    this.value += i;
   }
 }
 myNumber.add(1);```
⑵apply调用模式
apply:Function构造函数原型对象上的一个方法。构造函数的原型对象会被它创建的对象的原型链引用,而任何函数都是Function构造函数的实例,所以任何函数都可以直接调用apply方法。
apply功能:函数借用。将函数借用给一个对象,帮助它实现函数所定义的逻辑。
![apply调用模式,apply将p.move方法借用给circle这个对象](http://upload-images.jianshu.io/upload_images/316258-86b11eda2f4e8712.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

⑶函数调用模式的区别
函数执行时,JS引擎在函数本地作用域自动添加this、arguments两个临时变量,函数调用模式本质区别就体现在this变量的指向上。
![区别](http://upload-images.jianshu.io/upload_images/316258-e3a9cd3bf2c23447.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![arguments作用:用于获取函数实参
arguments[index]实参
arguments.length实参个数](http://upload-images.jianshu.io/upload_images/316258-2c25fa02ba161f1c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

>![思考题](http://upload-images.jianshu.io/upload_images/316258-caf17ed74d0f0d0e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

>1.this指向Window全局变量
2.不能
3.实现方法

//方法一:可以把helper调整为方法函数,这样helper就可以正确引用myNumber为this了
var myNumber = {
value:1,
helper:function(i) {
console.log(this);
this.value +=i;
},
add: function(i) {
this.helper(i);
}
}
myNumber.add(1);
//方法二:使用闭包
var myNumber = {
value: 1,
add: function(i){
var thisnew = this;
// 构建闭包
var helper = function(i){
console.log(thisnew);
thisnew.value += i;
}
helper(i);
}
}
//方法三:使用apply(call)调用模式,将当前helper方法借用给myNumber对象使用,这样this指向的就是myNumber对象
var myNumber = {
value: 1,
add: function(i){
var helper = function(i){
console.log(this);
this.value += i;
}
// myNumber对象借用helper方法,helper中的this将指向myNumber对象
helper.apply(myNumber,[i]); //apply方法
helper.call(myNumber,i); //call方法
}
}
//方法4,最笨的一种,针对这个题目只要不用this.value改成myNumber.value就可以了
var myNumber = {
value: 1,
add: function(i){
var helper = function(i);
console.log(this);
myNumber.value += i;
}
helper(i);
}
}
myNumber.add(1);```

5.函数传参
按值传递call by value:一个外部变量传递给一个函数时,函数实参获取的时这个外部变量的副本,在函数内部对实参修改,不会反映在外部变量。
引用传递:一个外部变量传递给一个函数时,函数实参实际上是这个外部变量的引用,在函数内部对实参修改,都会反应到外部变量。


【Q】网易微专业-JS16 函数进阶_第4张图片
函数传参
【Q】网易微专业-JS16 函数进阶_第5张图片
共享传递中,obj实际上获得的是count地址的一个副本,此时obj和count指向的是同一个对象,对这个对象属性的修改都会反映在obj和count上 而在函数内部将obj指向另一个对象时,并不会影响count
【Q】网易微专业-JS16 函数进阶_第6张图片
传参总结

6.闭包Closure
⑴函数内部定义的子函数用到了父函数变量所形成的特定的作用域。

??⑵闭包有哪些功能?

【Q】网易微专业-JS16 函数进阶_第7张图片
Paste_Image.png

基于js执行性能考虑,被频繁调用的函数内部定义和调用的帮助函数,如果不需要保存状态,应该将这些帮助函数保存到闭包作用域。

①闭包使用举例1

将字符串中的一些特定字符按顺序用数组中的元素替换,例如:
var arr = ['c','f','h','o'];
var str = 'ab4de8g4ijklmn7';
替换后 str == 'abcdefghijklmno';

 /*var arr = ['c','f','h','o'];
 var str = 'ab4de8g4ijklmn1';
 console.log(str);

 var func = (function(){
 // count变量会保存在闭包作用域内,表示func被调用次数(即正在替换第几个字符)
   var count = 0; 
   return function(){
     return arr[count++]; 
   }
 })();

 str = str.replace(/\d/g, func)
 console.log(str);```

②闭包使用举例2 -- 封装
1.暴露type类型和start, stop, getStatus方法
2.隐藏status,light对象状态

var Car = function(type){
var status = "stop",
light = "off";
return {
type: type,
start: function(){
status = "driving";
light = "on";
},
stop: function(){
status = "stop";
light = "off";
},
getStatus: function(){
console.log(type + " is " + status + " with light " + light);
}
}
}

var audi = new Car("audi");
audi.start();
audi.getStatus();
audi.stop();
audi.getStatus();```

③闭包使用举例3 -- 性能优化1
减少函数定义时间和内存消耗

// 不使用闭包
function sum(i, j) {
  var add = function(i, j){
    return i+j;
  }
  return add(i, j)
}
var startTime = new Date();
for(var i = 0; i< 1000000; i++) {
  sum(1,1);
}
var endTime = new Date();
console.log(endTime - startTime);

// // // 使用闭包
var sum = (function() {
  var add = function(i, j){
    return i+j;
  }
  return function(i,j) {
    add(i, j);
  }
})()
var startTime = new Date();
for(var i = 0; i< 1000000; i++) {
  sum(1,1);
}
var endTime = new Date();
console.log(endTime - startTime);```

闭包使用举例3 -- 性能优化2
普通递归函数跟使用闭包记录调用返回结果的递归函数调用次数对比

// // 普通递归函数
// var factorial = (function(){
// var count = 0;
// var fac = function(i){
// count++;
// if (i==0) {
// console.log('调用次数:' + count);
// return 1;
// }
// return i*factorial(i-1);
// }
// return fac;
// })();
// for(var i=0;i<=10;i++){
// console.log(factorial(i));
// }

// // 使用闭包记录调用返回结果的递归函数 -- 记忆函数
var factorial = (function(){
var memo = [1];
var count = 0;
var fac = function(i){
count++;
var result = memo[i];
if(typeof result === 'number'){
console.log('调用次数:' + count);
return result;
}
result = i*fac(i-1);
memo[i] = result;
return result;
}
return fac;
})();
for(var i=0;i<=10;i++){
console.log(factorial(i));
}```

7.first-class function:JS中函数可以当做普通变量来使用

【Q】网易微专业-JS16 函数进阶_第8张图片
Paste_Image.png

8.Function.prototype.bind 所有函数都可以调用bind方法
bind返回的是函数的引用。

9、函数柯里化??
函数柯里化通常是指把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的并且返回一个接受余下的参数而且返回结果的新函数的技术。

你可能感兴趣的:(【Q】网易微专业-JS16 函数进阶)