Skip to content

函数增强 ​

一、函数属性和 arguments ​

函数属性 ​

默认函数对象中已经有的属性。

  • name
  • length
javascript
function foo(a, b, c) {}

console.log(foo.name) // foo
console.log(foo.length) // 3

arguments ​

一个对应于传递给函数的参数的类数组(array-like)对象。

javascript
function foo() {
	console.log(arguments)
}

foo(12, 20, 34)

image-20230623173501901

array-like 意味着它不是一个数组类型,而是对象类型。

  • 但是它有数组的一些特性,比如 length,比如可以通过 index 索引访问
  • 但是没有数组的方法,比如 filter,map

argument 转数组,以便使用数组的特性:

  • Array.from

  • […arguments]

  • [].slice.apply(arguments)

剩余参数(rest) ​

es6 中引用了 rest parameter,可以将不定数量的参数放到一个数组中。

如果最后一个参数是 … 为前缀的,那么它会将剩余的参数放到该参数中,并且作为一个数组。

注意:剩余参数需要写到其他参数的最后。

javascript
function foo(num, str, ...args) {
	console.log(args)
}

foo(1, 'mocha', 123, 456) // [123, 456]

剩余参数和 argument 的区别:

  • 剩余参数只包含那些没有对应形参的实参,而 arguments 对象包含了传给函数的所有实参
  • arguments 对象不是一个真正的数组,而 rest 参数是一个真正的数组,可以进行数组的所有操作
  • arguments 是早期 ecmascript 中为了方便去获取所有的参数提供的一个数据结构,而 rest 参数是 es6 中提供并且希望以此来替代 arguments 的

二、纯函数的理解和应用 ​

确定的输入,一定会产出生确定的输出。

函数在执行过程中,不能产生副作用。

javascript
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' ]

三、柯里化的理解和应用 ​

javascript
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

自动柯里化函数:

javascript
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,这两个函数是依次执行的
  • 那么如果每次都需要进行两个函数的调用,操作上就会显得重复
  • 那么可以将这两个函数组合起来,自动依次调用
  • 这个过程就是对函数的组合,称之为组合函数
javascript
function double(num) {
	return num * 2
}

function pow(num) {
	return num ** 2
}

function composeFn(num) {
	return double(pow(num))
}

console.log(composeFn(4)) // 32

自动组合函数:

javascript
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 语句 ​

扩展一个语句的作用域链

javascript
var obj = {
	name: 'mocha',
	age: 18,
}

with (obj) {
	console.log(name) // mohca
	console.log(age) // 18
}

不建议使用 with 语句,以为它可能是混淆错误和兼容性问题的根源.

eval 函数 ​

运行执行一个代码字符串。

  • eval 是一个特殊的函数,它可以将传入的字符串当作 JavaScript 代码来执行

  • eval 会将最后一行执行语句的结果,作为返回值

javascript
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)

    image-20230703112623618

  • 也支持对某一个函数开启严格模式

    javascript
    function foo() {
    	'use strict'
    
    	m = 'foo'
    	console.log(m)
    }
    
    foo()

    image-20230703112713960

注意:

  • 没有类似于 "no use strict"这样的指令也可使程序返回默认模式
  • 现代 JavaScript 支持 "class" 和 "module",它们会自动启用 use strict

严格模式的语法限制:

JavaScript 被设计未新手开发者更容易上手,所以有时候本来错误语法,被认为也是可以正常被解析的

但是这种方式可能会带来安全隐患

在严格模式下,这种失误就会被当作错误,以便可以快速的发现和修正

  1. 无法意外的创建全局变量

    javascript
    'use strict'
    
    function foo() {
    	message = 'hello message'
    }
    
    foo()
    
    console.log(message) // ReferenceError: message is not defined
  2. 严格模式会使引起静默失败的赋值操作抛出异常(silently fail,不报错也没有任何效果)

    javascript
    'use strict'
    
    var obj = {
    	name: 'mocha',
    }
    
    Object.defineProperty(obj, 'name', {
    	configurable: false,
    })
    
    delete obj.name // 非严格模式下,此步不报错也没有任何效果,继续执行下一步;严格模式下抛出错误
    
    console.log(obj.name)

    image-20230703113559542

  3. 严格模式下试图删除不可删除的属性

    javascript
    'use strict'
    
    var obj = {
    	name: 'mocha',
    }
    
    Object.defineProperty(obj, 'name', {
    	writable: false,
    })
    
    obj.name = 'latte'
    
    console.log(obj.name)
  4. 严格模式不允许函数参数有相同的名称

    javascript
    'use strict'
    
    function foo(num, num) {}
  5. 不允许 0 的八进制语法

    javascript
    'use strict'
    
    console.log(0o123)
  6. 不允许使用 with

    image-20230703113727798

  7. eval 不再为上层引用变量

    javascript
    'use strict'
    
    eval(`var message = "hello message"`)
    
    console.log(message) // ReferenceError: message is not defined
  8. this 绑定不会默认转成对象

    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