Leetcode icon indicating copy to clipboard operation
Leetcode copied to clipboard

【深入理解JS核心技术】16. 什么是纯函数

Open webVueBlog opened this issue 2 years ago • 0 comments

纯函数是一个函数,其中返回值仅由其参数确定,没有任何副作用。即,如果您在应用程序中调用具有相同参数“n”次和“n”个位置的函数,那么它将始终返回相同的值。

我们举个例子来看看纯函数和非纯函数的区别

//Impure
let numberArray = [];
const impureAddNumber = (number) => numberArray.push(number);
//Pure
const pureAddNumber = (number) => (argNumberArray) =>
  argNumberArray.concat([number]);

//Display the results
console.log(impureAddNumber(6)); // returns 1
console.log(numberArray); // returns [6]
console.log(pureAddNumber(7)(numberArray)); // returns [6, 7]
console.log(numberArray); // returns [6]

请记住,纯函数很重要,因为它们简化了单元测试,没有任何副作用,也不需要依赖注入。它们还避免了紧密耦合,并且不会产生任何副作用,从而更难破坏您的应用程序。通过优先使用const而不是let使用,这些原则与 ES6 的不变性概念结合在一起。

函数内部

  1. arguments
  2. this
  3. caller
  4. new.target

函数内部,在ECMAScript5中,函数内部存在两个特殊的对象:arguments和this。ECMAScript6又新增了new.target属性。

arguments

arguments它是一个类数组对象,包含调用函数时传入的所有参数。这个对象只有以function关键字定义函数时才会有,箭头函数是没有的。

虽然主要用于包含函数参数,但arguments对象其实还有一个callee属性,是一个指向arguments对象所在函数的指针。

阶乘函数:

function factorial(num) {
 if (num <= 1) {
  return 1;
 } else {
  return num* factorial(num - 1);
 }
}

使用 arguments.callee 就可以让函数逻辑与函数名解耦:

function factorial(num) {
 if (num <= 1) {
  return 1;
 } else {
  return num * arguments.callee(num - 1);
 }
}

this

特殊的对象:this。在标准函数中,this引用的是把函数当成方法调用的上下文对象,这时候通常称其为this值(在网页的全局上下文中调用函数时,this指向windows)。

window.color = 'red'; 
let o = { 
 color: 'blue' 
}; 

function sayColor() { 
 console.log(this.color); 
} 

sayColor(); // 'red' 

o.sayColor = sayColor; 
o.sayColor(); // 'blue'

在箭头函数中,this引用的是定义箭头函数的上下文。

事件回调定时回调中调用某个函数时,this 值指向的并非想要的对象。使用箭头函数可以解决问题。

箭头函数中的this会保留定义该函数时的上下文。

function King() { 
 this.royaltyName = 'Henry'; 
 // this 引用 King 的实例
 setTimeout(() => console.log(this.royaltyName), 1000); 
}

function Queen() { 
 this.royaltyName = 'Elizabeth'; 
 // this 引用 window 对象
 setTimeout(function() { console.log(this.royaltyName); }, 1000); 
} 

new King(); // Henry 
new Queen(); // undefined

caller

ECMAScript5 也会给函数对象上添加一个属性:caller。虽然ECMAScript3中并没有定义,但所有浏览器除了早期版本的Opera都支持这个属性。

这个属性引用的是调用当前函数的函数,或者如果是在全局作用域中调用的则为null。

function outer() { 
 inner(); 
} 
function inner() { 
 console.log(inner.caller); 
} 
outer();

// 代码会显示 outer()函数的源代码
VM3252:5 ƒ outer() { 
 inner(); 
}

如果要降低耦合度,则可以通过 arguments.callee.caller 来引用同样的值。

function outer() { 
 inner(); 
} 

function inner() { 
console.log(arguments.callee.caller); 
} 

outer();

在严格模式下访问 arguments.callee 会报错。

ECMAScript 5 也定义了 arguments.caller,但在严格模式下访问它会报错,在非严格模式下则始终是 undefined。

严格模式下还有一个限制,就是不能给函数的 caller 属性赋值,否则会导致错误。

new.target

ECMAScript中的函数始终可以作为构造函数实例化一个新对象,也可以作为普通函数被调用。ECMAScript6新增了监测函数是否使用new关键字调用的new.target属性。

如果函数是正常的,则new.target的值是undefined;如果是使用new关键字调用的,则new.target将引用被调用的构造函数。

function King() { 
 if (!new.target) { 
 throw 'King must be instantiated using "new"' 
 } 
 console.log('King instantiated using "new"'); 
} 

new King(); // King instantiated using "new" 
King(); // Error: King must be instantiated using "new"

函数属性和方法

ECMAScript 中的函数是对象,因此有属性和方法。每个函数都有两个属性:length和 prototype。

prototype 是保存引用类型所有实例方法的地方,这意味着 toString()、valueOf()等方法实际上都保存在 prototype 上,进而由所有实例共享。

在 ECMAScript 5 中,prototype 属性是不可枚举的,因此使用 for-in 循环不会返回这个属性。

函数还有两个方法:apply()和 call()。

这两个方法都会以指定的 this 值来调用函数,即会设置调用函数时函数体内 this 对象的值。apply()方法接收两个参数:函数内 this 的值和一个参数数组。第二个参数可以是 Array 的实例,但也可以是 arguments 对象。通过 call()向函数传参时,必须将参数一个一个地列出来。

apply()和 call()真正强大的地方并不是给函数传参,而是控制函数调用上下文即函数体内 this 值的能力。

ECMAScript 5 出于同样的目的定义了一个新方法:bind()。bind()方法会创建一个新的函数实例,其 this 值会被绑定到传给 bind()的对象。

未完结!更多内容尽情期待下一节~

【深入理解JS核心技术】欢迎各位观众老爷,求点赞,求关注,求转发~

webVueBlog avatar May 02 '22 17:05 webVueBlog