@by Ruth92(转载请注明出处)

第4章:函数

一、JavaScript 中函数的两个重要特征

  1. 函数是第一类对象,可以作为带有属性和方法的值以及参数进行传递;
  2. 函数提供了局部作用域,而其他大括号并不能提供这种局部作用域。此外,声明的局部变量可被提升到局部作用域的顶部。

二、创建函数的语法包括:

  1. 命名函数表达式(函数表达式的一种特殊情况);

     var add = function add(a,b) {
    return a + b;
    }; // <—— 分号结尾
  2. 函数表达式,即匿名函数;

     // 该函数对象的 name 属性将会成为一个空字符串
    var add = function (a,b) {
    return a + b;
    }; // <—— 分号结尾
  3. 函数声明,与其他语言中的函数的语法类似。

     function foo() {
    // 函数主体
    }

注意,函数字面量 是指函数表达式或命名函数表达式,但不建议使用。

差别:

1)语法差异:

  • 函数声明不需要分号结尾;
  • 函数表达式需要。

2)用法差异:

  • 函数表达式应用场景:将函数对象作为参数传递,或者在对象字面量中定义方法。

      // 1. 作为参数传递
    callMe(function () {
    // 函数表达式(匿名函数)
    }) callMe(function me() {
    // 命名函数表达式
    }) // 2. 在对象字面量中定义方法
    var myObject = {
    say: function() {
    // 函数表达式
    }
    }
  • 函数声明只能出现在“程序代码”中,即仅能在其他函数体内部或全局空间中。其定义不能分配给变量或属性,也不能以参数形式出现在函数调用中。

      // 1.全局作用域
    function foo() {
    // 2.局部作用域
    function bar() {}
    return bar;
    }

3)函数的提升:

  • 对于所有变量,无论在函数体的何处进行声明,都会在后台被提升到函数顶部。

  • 当使用函数声明时,函数定义和函数声明都被提升。

      function hoistMe() {
    console.log(typeof foo); // 'function'
    console.log(typeof bar); // 'undefined' foo(); // 'local foo'
    bar(); // 'TypeError: bar is not a function' /*
    * 函数声明
    * 变量 'foo' 及其实现都被提升
    */
    function foo() {
    console.log('local foo');
    } /*
    * 函数表达式
    * 仅变量 'bar' 被提升,函数实现并未被提升
    */
    var bar = function() {
    console.log('local bar');
    }
    }

三、函数的模式:

1. API 模式——>可以为函数提供更好且更整洁的接口。

  • 回调模式:将函数作为参数进行传递;

注意点:回调与作用域,当回调作为一个对象的方法时,调用回调 this 会发生变化

使用场景:异步事件监听器、超时方法、库中的回调模式(有助于扩展及自定义库方法)。

/**
* 1.回调模式
* 功能逻辑解耦
*/
var findNodes() = function(callback) {
var i = 10000,
nodes = [],
found; // 检查回调函数是否为可调用的
if (typeof callback !== 'function') {
callback = false;
} while (i) {
i -= 1; // 运行回调函数
if (callback) {
callback(found);
} nodes.push(found);
} return nodes;
} /**
* 解决回调中存在的作用域问题——>当回调作为对象的方法时
* 解决方法:绑定作用域,传递该回调函数所属的对象
*/
var findNodes = function(callback, callback_obj) {
//...
if (typeof callback === 'function') {
callback.call(callback_obj, found);
}
} //使用=>
findNodes(myapp.paint, myapp); /**
* 更好的方案:无需重复两次输入对象的名称
*/
var findNodes = function(callback, callback_obj) {
if (typeof callback === 'string') {
callback = callback_obj[callback];
} //...
if (typeof callback === 'function') {
callback.call(callback_obj, found);
}
//...
} //使用=>
findNodes('paint', myapp); /**
* 应用场景:
*/
// 异步事件监听器
document.addEventListener('click', console.log, false); // 超时
var thePlotThickens = function() {};
setTimeout(thePlotThickens, 500); //<=回调不带括号,带括号表示立即执行
  • 配置对象:有助于保持收到控制的函数的参数数量;

缺点:需要记住参数名称;属性名称无法被压缩

应用:当函数创建DOM元素时,该模式非常有用,如,可用于设置元素的CSS样式。

/**
* 配置对象
*/
var conf = {
username: 'batman',
first: 'Bruce',
last: 'Wayne'
}; addPerson(conf);
  • 返回函数:当一个函数的返回值是另一个函数时;

闭包

/**
* 闭包
*/
var setup = function() {
var count = 0;
return function() {
return (count += 1);
}
} var next = setup();
next(); // 1
next(); // 2
  • Curry 化:是一个转化过程,即我们执行函数转换的过程。当新函数是基于现有函数,并加上部分参数列表创建时。

函数应用:在一些纯粹的函数式编程语言中,函数并不描述为被调用(called 或 invoked),而是描述为应用(applied)。

函数调用:实际上就是将一个参数集合应用到一个函数中。

Curry过程:使函数理解并处理部分应用的过程。

// 定义函数
var sayHi = function(who) {
return "Hello" + (who ? "," + who : "") + "!";
}; // 调用函数
sayHi(); //'Hello'
sayHi('world'); // 'Hello, world' // 应用函数1
sayHi.apply(null, ["hello"]); // 'Hello, hello!' // 应用函数2:使用 call() 方法比 apply() 更有效率,节省了一个数组
sayHi.call(null, 'hello'); // 'Hello, hello!' /**
* curry化的add()函数
* 接受部分参数列表
*/
// x 隐式地存储在闭包中,并且还将 y 作为局部变量复用
function add(x,y) {
if (typeof y === 'undefined') { // 部分
return function(y) {
return x + y;
}
}
// 完全应用
return x + y;
} typeof add(5); // 'function'
add(3)(4); // 7
// 创建并存储一个新函数
var add2000 = add(2000);
add2000(10); // '2010' /**
* 通用curry化函数示例
*/
function schonofinkelize(fn) {
var slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
return function() {
var new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
}
} // 普通函数
function add(x, y) {
return x + y;
} // 将一个函数curry化以获得一个新的函数
var newadd = schonofinkelize(add, 5);
newadd(4); // 9 //另一种选择——直接调用新函数
schonofinkelize(add, 6)(7);

2. 初始化模式——>可以在不污染全局命名空间的情况下,使用临时变量以一种更加整洁、结构化的方式初始化以及设置任务。

  • 即时函数:只要定义之后就立即执行;

      /**
    * 即时函数
    */
    // 写法1——>JSLint偏好该写法
    (function() {
    //...
    }()); // 写法2
    (function() { })(); /**
    * 即时函数传参
    */
    (function(global) {
    // 通过 'global' 访问全局变量
    }(this)); /**
    * 即时函数的返回值
    */
    // 写法1——>推荐
    var result = (function(){
    return 2 + 2;
    }()); // 写法2
    var result = function() {
    return 2 + 2;
    }(); // 写法3
    var result = (function(){
    return 2 + 2;
    })();
  • 即时对象初始化:匿名对象组织了初始化任务,提供了可被立即调用的方法;

缺点:不利于压缩

/**
* 即时对象初始化
*/
({
// 在这里可以定义设定值
// 又名配置常数
maxwidth: 600,
maxheight: 400, // 还可以定义一些实用的方法
gimmeMax: function() {
return this.maxwidth + 'x' + this.maxheight;
}, // 初始化
init: function() {
console.log(this.gimmeMax());
// 更多初始化任务
}
}).init(); // 语法
({...}).init();
({...}.init()); // 返回对对象的引用:在init()函数尾部添加 "return this;"
  • 初始化时分支(加载时分支):是一种优化模式,帮助分支代码在初始化代码执行过程中仅检测一次,这与以后在程序生命期内多次检测相反。

      /**
    * 初始化时分支
    */
    // 之前
    var utils = {
    addListener: function(el, type, fn) {
    if (typeof window.addEventListener === 'function') {
    el.addEventListener(type, fn, false);
    } else if (typeof document.attachEvent === 'function') {
    el.attachEvent('on' + type, fn);
    } else {
    el['on' + type] = fn;
    }
    },
    removeListener: function(el, type, fn) {
    // 几乎一样...
    }
    }; // 之后:可以在脚本初始化加载时一次性探测出浏览器特征 // 接口
    var utils = {
    addListener: null,
    removeListener: null
    }
    // 实现
    if (typeof window.addEventListener === 'function') {
    utils.addListener = function(el, type, fn) {
    el.addEventListener(type, fn, false);
    };
    utils.removeListener = function(el, type, fn) {
    el.removeEventListener(type, fn, false);
    }; // 判断为IE浏览器
    } else if (typeof document.attachEvent === 'function') {
    utils.addListener = function(el, type, fn) {
    el.attachEvent('on' + type, fn);
    };
    utils.removeListener = function(el, type, fn) {
    el.detachEvent('on' + type, fn);
    } // 更早版本浏览器
    } else {
    utils.addListener = function(el, type, fn) {
    el['on' + type] = fn;
    };
    utils.removeListener = function(el, type, fn) {
    el['on' + type] = null;
    }
    }

3. 性能模式——>可以加速代码运行

  • 备忘模式:使用函数属性以便使得计算过的值无须再次计算;

默认:length 属性,包含了该函数期望的参数数量

自定义属性:缓存函数结果(备忘)

/**
* 函数属性
*/
var myFunc = functino(param) {
if (!myFunc.cache[param]) {
var result = {};
//... 开销很大的操作 ...
myFunc.cache[param] = result;
} return myFunc.cache[param];
}; // 缓存存储
myFunc.cache = {}; /**
* 优化点
*/
var myFunc = functino() { // 1.参数优化:如果有更多及复杂的参数,可以将它们序列化为一个JSON字符串
var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)),
result; // 2.使用 arguments.callee 来引用该函数,在ES5的严格模式中不支持
var f = arguments.callee; if (!f.cache[cachekey]) {
var result = {};
//... 开销很大的操作 ...
f.cache[cachekey] = result;
} return f.cache[cachekey];
}; // 缓存存储
myFunc.cache = {};
  • 自定义模式:以新的主体重写本身,以使得在第二次或以后调用时仅需执行更少的工作;

缺点:

当它重定义自身时已经添加到原始函数的任何属性都会丢失;

此外,如果函数使用了不同的名称,比如分配给不同的变量或者以对象的方法来使用,那么重定义部分将永远不会发生,并且将会执行原始函数体

/**
* 自定义函数(惰性函数自定义):可以更新自身的实现
* 当有一些初始化准备工作要做,并且仅需要执行一次,该模式就非常有用
*/
var scareMe = function() {
console.log('Boo!');
scareMe = function() {
console.log('Double boo!');
};
}; scareMe(); // 'Boo!'
scareMe(); // 'Double boo!' /**
* 自定义函数的缺点:
* 1)当它重定义自身时已经添加到原始函数的任何属性都会丢失;
* 2)如果函数使用了不同的名称,比如分配给不同的变量或者以对象的方法来使用,那么重定义部分将永远不会发生,并且将会执行原始函数体
*/
// 1.添加一个新的属性
scareMe.property = "properly"; // 2.赋值给另一个不同名称的变量
var prank = scareMe; // 3.作为一个方法使用
var spooky = {
boo: scareMe
}; prank(); // 'Boo!'
prank(); // 'Boo!'
console.log(prank.property); // 'properly' spooky.boo(); // 'Boo!'
spooky.boo(); // 'Boo!'
console.log(spooky.boo.property); // 'properly' scareMe(); // 'Double boo!'
scareMe(); // 'Double boo!'
console.log(scareMe.property); // 'undefined'

《JavaScript模式》第4章 函数的更多相关文章

  1. javascript进阶课程--第一章--函数

    javascript进阶课程--第一章--函数 学习要点 了解内存管理 掌握全局函数的使用 知识点 基本类型和引用类型 基本类型值有:undefined,NUll,Boolean,Number和Str ...

  2. Javascript模式(第二章基本技巧)------读书笔记

    本章主要帮助大家写出高质量的JS代码的方法,模式和习惯,例如:避免使用全局变量,使用单个的var变量声明,缓存for循环的长度变量length等 一.尽量避免使用全局变量 1 每一个js环境都有一个全 ...

  3. Javascript模式(第一章简介)------读书笔记

    一:模式 模式是一个通用问题的解决方案,可以提供一个更好的实践经验.有用的抽象化表示和解决一类问题的模板. 本书主要讨论如下三种类型的模式 1 设计模式:可复用面向对象软件的基础,包括singleto ...

  4. Javascript模式(第四章函数)------读书笔记

    一 背景 js函数的两个特点:1 函数是第一类对象(first-class object):2 函数可以提供作用域 1 函数是对象: 1 函数可以在运行时动态创建,还可以在程序执行过程中创建 2 可以 ...

  5. Javascript模式(第五章对象创建模式)------读书笔记

    一 命名空间模式 1 命名空间模式的代码格式 var MYAPP={ name:"", version:"1.0", init:function(){ } }; ...

  6. 【读书笔记】读《JavaScript模式》 - 函数复用模式之现代继承模式

    现代继承模式可表述为:其他任何不需要以类的方式考虑得模式. 现代继承方式#1 —— 原型继承之无类继承模式 function object(o) { function F() {}; F.protot ...

  7. 【读书笔记】读《JavaScript模式》 - 函数复用模式之类式继承模式

    实现类式继承的目标是通过构造函数Child()获取来自于另外一个构造函数Parent()的属性,从而创建对象. 1.类式继承模式#1 —— 默认方式(原型指向父函数实例) function Paren ...

  8. 精读JavaScript模式(五),函数的回调、闭包与重写模式

    一.前言 今天地铁上,看到很多拖着行李箱的路人,想回家了. 在上篇博客结尾,记录到了函数的几种创建方式,简单说了下创建差异,以及不同浏览器对于name属性的支持,这篇博客将从第四章函数的回调模式说起. ...

  9. JavaScript进阶 - 第5章 小程序,大作用(函数)

    5-1什么是函数 函数的作用,可以写一次代码,然后反复地重用这个代码. 如:我们要完成多组数和的功能. var sum;   sum = 3+2; alert(sum);   sum=7+8 ; al ...

  10. 《JavaScript模式》读书笔记

    简介 在软件开发过程中,模式是指一个通用问题的解决方案.一个模式不仅仅是一个可以用来复制粘贴的代码解决方案,更多地是提供了一个更好的实践经验.有用的抽象化表示和解决一类问题的模板. 对象有两大类: 本 ...

随机推荐

  1. HashCode

    如果一个类的对象要用做hashMap的key,那么一定要注意覆盖该类的equals和hashCode方法. equals()是基类Object的方法,用于判断对象是否有相同地址及是否为同一对象 pub ...

  2. 如何修改mysql用户名和密码

    如何修改mysql用户名和密码   以修改mysql的root密码为例修改的三种方法 方法1: 用SET PASSWORD命令 mysql>SET PASSWORD FOR 'root'@'lo ...

  3. Mac下用g++编译opencv程序报错

    问题描述: 在Mac下安装好opencv, 安装:    bash  brew install opencv      写了一个opencv程序:    ``` C++ //作用就是:取视频的每一帧, ...

  4. kvm -- Kernel-based Virtual Machine

    1.虚拟机类型: 类型1 硬件上直接安装hp  类型2 硬件上安装HOST 上面跑VMM 2.kvm概要 kvm 不算类型1也不算类型二.两种特性都有,他是linux的一个内核模块,内核中本身没有hv ...

  5. WIN10图标显示异常

    1.打开控制面板,右上边查看方式选择边小图标显示 2.打开显示里面的自定义缩放级别 3.设置级别为125%  然后点击 应用 4.注销或者重启,然后将异常的快捷方式点击打开,就会恢复正常,至少我的是回 ...

  6. (转)awk实例练习(一)

    文章转自 http://www.cnblogs.com/zhuyp1015/archive/2012/07/14/2591822.html 前一篇学习了awk的基本知识,现在来做一些练习加深一下印象. ...

  7. Scrum Meeting 3-20151203

    任务安排 姓名 今日任务 明日任务 困难 董元财 请假(明天是编译截至最后一天) 学习上拉加入新的listview 无 胡亚坤 请假(明天是编译截至最后一天) 设计优化聊天页面 无 刘猛 请假(明天是 ...

  8. switch多分支语句简析

    在编程中一个常见问题就是检测一个变量是否符合某个条件,switch以一个简单明了的方式来实现类似于"多选一"的选择,语法格式如下: /*switch首先计算表达式的值,如果表达式的 ...

  9. 程序设计入门——C语言 第4周编程练习 2 念整数(5分)

    题目内容: 你的程序要读入一个整数,范围是[-100000,100000].然后,用汉语拼音将这个整数的每一位输出出来. 如输入1234,则输出: yi er san si 注意,每个字的拼音之间有一 ...

  10. html知识——表单

    1.表单标记:<form>内容</form> 属性:     name(表单名称) 必须写!!!  method(传送数据方式): get--将表单的内容附加在URL地址的后面 ...