1、理解JavaScript纯函数
1.1、概念
- 确定的输入,一定会产生确定的输出
- 函数在执行过程中,不能产生副作用
副作用的理解:
在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加影响,比如修改了全局变量,修改参数或者改变外部的存储。
1.2、纯函数的举例
- 一
function foo(num1, num2) {
return num1 * 2 + num2 *3;
}
- 二
function test(info) {
return {
...info,
age: 100
};
}
- 三:数组的
slice
方法
2、JavaScript柯里化
2.1、概念
只传递给函数一部分参数来调用它,让它返回一个函数去处理剩余的参数,这个过程就称之为柯里化。
function foo(m, n, x, y) {
return m + n + x + y;
}
foo(10, 20, 30, 40);
// 柯里化过程:把foo转换为bar的过程就叫做柯里化
function bar(m) {
return function(n) {
return function(x) {
return function(y) {
return m + n + x + y;
}
}
}
}
bar(10)(20)(30)(40);
// 简化柯里化的代码
m => n => x => y => m + n + x + y;
2.2、 为什么需要有柯里化
2.2.1、单一职责的原则
- 在函数式编程中,我们其实往往希望一个函数处理的问题尽可能的单一,而不是将一大堆的处理过程交给一个函数来处理;
- 那么我们是否就可以将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果。
function sum(x) {
x = x + 2;
return function(y) {
y = y* 2;
return function(z) {
z = z * z;
return x + y + z;
}
}
}
2.2.2、逻辑的复用
- 举例
function sum(m) {
m = m * m; // 复用的逻辑
return function(n) {
return m + n;
}
}
var addNum = sum(5);
addNum(10);
addNum(20);
2.3、柯里化函数的实现
function hyCurrying(fn) {
return function curried(...args) {
// 判断当前已经接收的参数的个数(args.length),和函数(fn)本身需要接收的参数个数(fn.length)是否一致了
if (args.length >= fn.length) {
// fn(...args);
// 保证调curried函数的this跟调fn函数的this是一样的
// fn.call(this, ...args);
return fn.apply(this, args);
} else {
// 没有达到个数时,需要返回一个新的函数,继续来接收新的参数
return function curried2(...args2) {
// 接收到参数后,需要递归调用curried来检查函数的个数是否达到
return curried.apply(this, [...args, ...args2]);
}
}
}
}
function add(x, y, z) {
return x + y + z;
}
var curryAdd = hyCurrying(add);
curryAdd(10, 20, 30);
curryAdd(10, 20)(30);
curryAdd(10)(20)(30);
3、组合函数
3.1、概念
组合(Compose)函数是在js开发过程中一种对函数的使用技巧、模式:
- 比如我们现在需要对某一个数据进行函数的调用,执行两个函数fn1和fn1,这两个函数是依次执行的
- 那么如果每次我们都需要进行两个函数的调用,操作上就会显得重复
- 那么是否可以将这两个函数组合起来,自动依次调用呢?
- 这个过程就是对函数的组合,我们称之为组合函数(Compose Function)
3.2、举例
- 简单举例
function double(num) {
return num * 2;
}
function square(num) {
return num * num;
}
var count = 10;
var result = square(double(count));
console.log(result);
function composeFn(m, n) {
return function(count) {
return n(m(count));
}
}
var newFn = composeFn(double, square);
console.log(newFn(10));
- 通用实现
function hyCompose(...fns) {
var length = fns.length;
for (var i = 0; i < length; i++) {
if (typeof fns[i] !== 'function') {
throw new Typeerror("Expected arguments are functions")
}
}
return function compose(...args) {
var index = 0;
var result = length ? fns[index].apply(this, args) : args;
while(++index < length) {
result = fns[index].call(this, result);
}
return result;
}
}
function double(m) {
return m * 2;
}
function square(n) {
return n * 2;
}
var newFn = hyCompose(double, square);
console.log(newFn(10, 2));
4、with语句 —— 可以形成自己的作用域
注:了解就可以,现在已经不推荐使用了。在严格模式下使用会报错。
var message = "Hello World";
// with语句:可以形成自己的作用域
var obj = {name: "why", age: 18, message: "obj message"};
function foo() {
function bar() {
with(obj) {
// 查找作用域链:obj -> bar -> foo -> 全局
console.log(message);
}
}
bar();
}
foo();
5、eval函数
注:了解就可以。
eval
是一个特殊的函数,它可以将传入的字符串当做hs代码来执行。
var jsString = 'var message = "Hello World";console.log(message);';
eval(jsString);
不建议在开发中使用eval:
- eval代码的可读性非常差(代码的可读性时高质量代码的重要原则)
- eval是一个字符串,那么有可能在执行的过程中被刻意篡改,那么可能会造成被攻击的风险
- eval的执行必须经过js解释器,不能被js引擎优化
6、严格模式
6.1、概念
在ES5标准中,js提出了严格模式的概念(Strict Mode):
- 严格模式很好理解,是一种具有限制性的js模式,从而使代码隐式的脱离了“懒散(sloppy)模式”
- 支持严格模式的浏览器在检测到代码中有严格模式时,会以更加严格的方式对代码进行检测和执行
"use strict";
6.2、严格模式下常见的限制
6.2.1、禁止意外创建全局变量
"use strict";
message = 'hello world';
console.log(message);
function foo() {
age = 20;
}
foo();
console.log(age);
6.2.2、不允许函数有相同的参数名称
"use strict";
function foo(x, y, x) {
// 30 20 30 后面的x把前面的x覆盖了
console.log(x, y, x);
}
foo(10, 20, 30);
6.2.3、静默错误
"use strict";
true.name = 'abc';
NaN = 123;
var obj = {};
Object.defineProperty(obj, 'name', {
// 不可配置(严格模式下该属性不能通过delete删除)
configurable: false,
// 只读(严格模式下不能进行赋值操作)
writable: false,
value: 'why'
});
console.log(obj.name)
obj.name = 'kobe';
delete obj.name;
6.2.4、不允许使用原先的八进制格式
"use strict";
// 原来的八进制写法:以数字0开头
var num = 0123;
console.log(num);
补充:es6后的写法
var num = 0o123; // 八进制
var num2 = 0x123; // 十六进制
var num3 = 0b100; // 二进制
6.2.5、with语句不允许使用
6.2.6、eval韩式不会向上引用变量
"use strict";
var jsString = 'var message = "hello world"; console.log(message);'
eval(jsString);
console.log(message); // 报错:message is not defined
*6.2.7、严格模式下的this
在严格模式下,自执行函数(默认绑定)会指向undefined
。
"use strict";
function foo() {
console.log(this); // undefined
}
foo();
强调:setTimeout
在严格/非严格的模式下,传入的回调函数(非箭头函数的情况下)的this总是指向window
的。伪代码:
setTimeout(fn, delay);
// fn的调用方式
fn.apply(this); // this总是指向window