《JavaScript核心技术开发解密》读书笔记(一)
《JavaScript核心技术开发解密》读书笔记(二)
《JavaScript核心技术开发解密》读书笔记(三)
《JavaScript核心技术开发解密》读书笔记(四)
《JavaScript核心技术开发解密》读书笔记(五)
下面是第八章–函数与函数式编程
函数
函数形式大概有四种,分别是函数声明、函数表达式、匿名函数与自执行函数
1、函数声明
函数声明是指利用关键字 function 来声明一个函数。在变量对象的创建过程中,function声明的函数比var声明的变量有更加优先的执行顺序,即函数声明提前(提升)。因此在同一执行上下文中,无论在什么地方声明了函数,都可以直接使用。
function fn() {
console.log('function') ;
}
2、函数表达式
函数表达式是将一个函数体赋值给一个变量的过程。
var fn = function () {}
上面代码的执行步骤如下:
其中,把var fn = undefined会因为变量对象的原因而提前执行,即变量提升,因此使用函数表达式时,要注意代码的先后顺序。
var fn = undefined;
fn = function () {}
3、匿名函数
匿名函数即没有名字的函数,一般会作为一个参数或者一个返回值来使用,通常不使用变量来保存它的引用。
常见的匿名函数使用和场景:
// 1.setTimeout中的参数
var timer = setTimeout(function () {
console.log('timer')
}, 1000)
// 2.数组方法中的参数
var arr = [1,2,3];
var arr2 = arr.map(function (item) {
return item + 1;
})
// 3.匿名函数作为一个返回值
function add() {
var a = 10;
return function () {
return a + 10
}
}
add()();
4、自执行函数
函数会产生独立的作用域,因此可以利用自执行函数来模拟块级作用域。
(function () {
// 一些逻辑
})()
当我们使用自执行函数创建一个模块时,也就意味着,外界已经无法访问该模块内部的任何属性与方法了。好在有闭包,作为模块之间能相互交流的桥梁,让模块能够在我们的控制之下,选择性地对外开发属性与方法。
(function() {
// 私有变量
var age = 22;
// 私有方法
function getAge() {
return age;
}
// 将引用保存在外部执行环珑的变量中,这是一种简单的对外开发方法的方式
window.getAge = getAge;
})();
函数式编程
函数式编程,即函数封装
纯函数
相同的输入总会得到相同的输出,并且不会产生副作用的函数,就是纯函数。
var source = [1, 2, 3, 4, 5];
source.slice(1, 3); // 纯函数返回[2, 3] , source 不变
source.splice(1, 3); // 不纯 函数返 回[2, 3, 4] , source被改变了
source.pop(); // 不纯
source.push(6); // 不纯的
source.shift(); // 不纯的
source.unshift(1); // 不纯的
source.reverse(); // 不纯的
//不能短时间知道现在source 被改变成什么样子了,干脆重新约定 一下
source = [1, 2, 3, 4, 5];
source.concat([6, 7]); // 纯函数返回[1, 2, 3, 4, 5, 6, 7], source不变
source.join('-'); // 纯函数返回1-2-3-4-5, source不变
高阶函数
简单粗暴一点来理解,则凡是接收一个函数作为参数的函数,就是高阶函数。
模拟关键字new
// 将构造函数以参数的形式传入
function New(func) {
// 声明一个中间对象,该对象为最终返回的实例
var res = {};
if (func.prototype !== null) {
// 将实例的原型指向构造函数的原型
res.__proto__ = func.prototype;
}
// ret 为构造函数的执行结果,这里通过apply,将构造函数内部的this指向修改为指向res,即为实例对象
var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
// 当在构造函数中明确指定了返回对象时,那么new的执行结果就是该返回对象
if ((typeof ret === 'object' || typeof ret === 'function') && ret !== null) {
return ret;
}
// 如果没有明确指定返回对象,则默认返回res,这个res就是实例对象
return res;
}
实现数组的map方法
Array.prototype._map = function (fn, context) {
// 首先定义一个数组来保存每一项的运算结果,最后返回
var temp = [];
if (typeof fn == 'function') {
var k = 0;
var len = this.length;
for (; k < len; k++) {
// 将每一项的运算操作丢进fn里,利用call方法指定fn的this指向与具体参数
temp.push(fn.call(context, this[k], k, this));
}
} else {
console.error('TypeError: '+ fn +'is not a function.');
}
// 返回每一项运算结果组成的新数组
return temp;
}
柯里化
柯里化是指这样一个函数(假设叫作createCurry),它接收函数A作为参数徐,运行后能够返回一个新的函数,并且这个新的函数能够c处理函数A的剩余参数。
// arity 用来标记剩余参数的个数
// args 用来收集参数
function createCurry(func, arity, args) {
// 第一次执行时,并不会传入arity,而是直接获取func参数的个数func.length
var arity = arity || func.length;
//第一次执行也不会传入args,而是默认为空数组
var args = args || [];
var wrapper = function() {
// 将wrapper中的参数收集到args中
var _args = [].slice.call(arguments);
[].push.apply(args, _args);
// 如果参数个数小于最初的func.length,则递归调用,继续收集参数
if (_args.length < arity) {
arity -= _args.length;
return createCurry(func, arity, args);
}
// 参数收集完毕,执行func
return func.apply(func, args);
}
return wrapper;
}