【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.函数定义之间的区别


Paste_Image.png
Paste_Image.png

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


函数调用模式

⑴方法调用模式

 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:一个外部变量传递给一个函数时,函数实参获取的时这个外部变量的副本,在函数内部对实参修改,不会反映在外部变量。
引用传递:一个外部变量传递给一个函数时,函数实参实际上是这个外部变量的引用,在函数内部对实参修改,都会反应到外部变量。


函数传参
共享传递中,obj实际上获得的是count地址的一个副本,此时obj和count指向的是同一个对象,对这个对象属性的修改都会反映在obj和count上 而在函数内部将obj指向另一个对象时,并不会影响count
传参总结

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

??⑵闭包有哪些功能?

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中函数可以当做普通变量来使用

Paste_Image.png

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容