函数增强
一、函数属性和 arguments
函数属性
默认函数对象中已经有的属性。
- name
- length
function foo(a, b, c) {}
console.log(foo.name) // foo
console.log(foo.length) // 3arguments
一个对应于传递给函数的参数的类数组(array-like)对象。
function foo() {
console.log(arguments)
}
foo(12, 20, 34)
array-like 意味着它不是一个数组类型,而是对象类型。
- 但是它有数组的一些特性,比如 length,比如可以通过 index 索引访问
- 但是没有数组的方法,比如 filter,map
argument 转数组,以便使用数组的特性:
Array.from
[…arguments]
[].slice.apply(arguments)
剩余参数(rest)
es6 中引用了 rest parameter,可以将不定数量的参数放到一个数组中。
如果最后一个参数是 … 为前缀的,那么它会将剩余的参数放到该参数中,并且作为一个数组。
注意:剩余参数需要写到其他参数的最后。
function foo(num, str, ...args) {
console.log(args)
}
foo(1, 'mocha', 123, 456) // [123, 456]剩余参数和 argument 的区别:
- 剩余参数只包含那些没有对应形参的实参,而 arguments 对象包含了传给函数的所有实参
- arguments 对象不是一个真正的数组,而 rest 参数是一个真正的数组,可以进行数组的所有操作
- arguments 是早期 ecmascript 中为了方便去获取所有的参数提供的一个数据结构,而 rest 参数是 es6 中提供并且希望以此来替代 arguments 的
二、纯函数的理解和应用
确定的输入,一定会产出生确定的输出。
函数在执行过程中,不能产生副作用。
var names = ['abc', 'cba', 'nba', 'mba']
// 1、slice:纯函数 返回新的数组,对原数组无影响
var newNames = [].slice.apply(names, [1, 3])
console.log(newNames) // [ 'cba', 'nba' ]
console.log(names) // [ 'abc', 'cba', 'nba', 'mba' ]
// 2、splice: 非纯函数,直接操作数组
names.splice(2, 2)
console.log(names) // [ 'abc', 'cba' ]三、柯里化的理解和应用
function foo1(x, y, z) {
console.log(x + y + z)
}
// 函数柯里化
function foo2(x) {
return function (y) {
return function (z) {
console.log(x + y + z)
}
}
}
foo2(10)(20)(30)
// 箭头函数简写方式
var foo3 = x => y => z => console.log(x + y + z)
foo3(10)(20)(30) // 60自动柯里化函数:
function foo(x, y, z) {
console.log(x + y + z)
}
// 自动柯里化
function curring(fn) {
function curryFn(...args) {
// 两类操作
// 第一类操作:继续返回一个新的函数,继续接收参数
// 第二类操作:直接执行fn函数
if (args.length >= fn.length) {
// 执行第二类
// return fn(...args)
return fn.apply(this, args)
} else {
// 执行第一类
return function (...newArgs) {
// return curryFn(...args.concat(newArgs))
return curryFn.apply(this, args.concat(newArgs))
}
}
}
return curryFn
}
var fooCurry = curring(foo)
fooCurry(10)(20)(30) // 60四、组合函数理解和应用
组合函数是在 JavaScript 开发过程中对函数使用的一种技巧,模式:
- 比如现在需要对某一个数据进行函数的调用,执行两个函数 fn1 和 fn2,这两个函数是依次执行的
- 那么如果每次都需要进行两个函数的调用,操作上就会显得重复
- 那么可以将这两个函数组合起来,自动依次调用
- 这个过程就是对函数的组合,称之为组合函数
function double(num) {
return num * 2
}
function pow(num) {
return num ** 2
}
function composeFn(num) {
return double(pow(num))
}
console.log(composeFn(4)) // 32自动组合函数:
function composeFn(...fns) {
// 1、边界判断(edge case)
var length = fns.length
if (length <= 0) return
for (var i = 0; i < length; i++) {
if (typeof fns[i] !== 'function') {
throw new Error(`index position ${i + 1} must be function`)
}
}
// 2、返回的新函数
return function (...args) {
var result = fns[0].apply(this, args)
for (var i = 1; i < length; i++) {
var fn = fns[i]
result = fn.apply(this, [result])
}
return result
}
}
function double(num) {
return num * 2
}
function pow(num) {
return num ** 2
}
var newFn = composeFn(double, pow)
console.log(newFn(100)) // 40000五、with,eval 的使用
with 语句
扩展一个语句的作用域链
var obj = {
name: 'mocha',
age: 18,
}
with (obj) {
console.log(name) // mohca
console.log(age) // 18
}不建议使用 with 语句,以为它可能是混淆错误和兼容性问题的根源.
eval 函数
运行执行一个代码字符串。
eval 是一个特殊的函数,它可以将传入的字符串当作 JavaScript 代码来执行
eval 会将最后一行执行语句的结果,作为返回值
var evalString = `var message = "hello world"; console.log(message)`
eval(evalString) // hello world不建议在开发中使用 eval:
- eval 代码的可读性非常差(代码的可读性是高质量代码的重要原则)
- eval 是一个字符串,那么有可能在执行的过程中被刻意篡改,造成被攻击的风险
- eval 的执行必须经过 JavaScript 解释器,不能被 JavaScript 引擎优化
六、严格模式的使用
JavaScript 历史的局限性:
- 长久以来,JavaScript 不断向前发展并且未带来任何兼容性问题
- 新的特性被加入,旧的功能也没有改变,这么做有利于兼容旧代码
- 但缺点是 JavaScript 创造者的任何错误或不完善的决定也将永远被保留在 JavaScript 语言中
在 ECMAScript5 标准中,JavaScript 提出了严格模式的概念(Strict Mode)
- 严格模式很好理解,是一种具有限制性的 JavaScript 模式,从而使代码隐式的脱离了“懒散(sloppy)模式”
- 支持严格模式的浏览器在检测到代码中有严格模式时,会以更加严格的方式对代码进行检测和执行
严格模式对正常的 JavaScript 语义进行了一些限制:
- 严格模式通过抛出错误来消除一些静默错误
- 严格模式让 JS 引擎在执行代码时可以进行更多的优化(不需要对一些特殊的语法进行处理)
- 严格模式禁用了在 ECMAScript 未来版本中可能会定义的一些语法
开启严格模式
可以支持在 js 文件中开启严格模式
JavaScript// javascript文件 "use strict" console.log(name)
也支持对某一个函数开启严格模式
javascriptfunction foo() { 'use strict' m = 'foo' console.log(m) } foo()
注意:
- 没有类似于 "no use strict"这样的指令也可使程序返回默认模式
- 现代 JavaScript 支持 "class" 和 "module",它们会自动启用 use strict
严格模式的语法限制:
JavaScript 被设计未新手开发者更容易上手,所以有时候本来错误语法,被认为也是可以正常被解析的
但是这种方式可能会带来安全隐患
在严格模式下,这种失误就会被当作错误,以便可以快速的发现和修正
无法意外的创建全局变量
javascript'use strict' function foo() { message = 'hello message' } foo() console.log(message) // ReferenceError: message is not defined严格模式会使引起静默失败的赋值操作抛出异常(silently fail,不报错也没有任何效果)
javascript'use strict' var obj = { name: 'mocha', } Object.defineProperty(obj, 'name', { configurable: false, }) delete obj.name // 非严格模式下,此步不报错也没有任何效果,继续执行下一步;严格模式下抛出错误 console.log(obj.name)
严格模式下试图删除不可删除的属性
javascript'use strict' var obj = { name: 'mocha', } Object.defineProperty(obj, 'name', { writable: false, }) obj.name = 'latte' console.log(obj.name)严格模式不允许函数参数有相同的名称
javascript'use strict' function foo(num, num) {}不允许 0 的八进制语法
javascript'use strict' console.log(0o123)不允许使用 with

eval 不再为上层引用变量
javascript'use strict' eval(`var message = "hello message"`) console.log(message) // ReferenceError: message is not definedthis 绑定不会默认转成对象
javascript'use strict' function foo() { console.log(this) } foo.apply('abc') // abc foo.apply(123) // 123 foo.apply(undefined) // undefined foo.apply(null) // null // 独立函数调用时,绑定window对象 // 严格模式下,不绑定全局对象而是绑定undefined foo() // undefined