Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

memoize-one

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

memoize-one - npm Package Compare versions

Comparing version 1.0.0-rc.1 to 1.0.0

5

lib/index.js

@@ -10,2 +10,3 @@ "use strict";

var lastThis = void 0;
var lastArgs = [];

@@ -15,2 +16,3 @@ var lastResult = void 0;

// breaking cache when arguments or context changes
return function () {

@@ -21,3 +23,3 @@ for (var _len = arguments.length, newArgs = Array(_len), _key = 0; _key < _len; _key++) {

if (calledOnce && newArgs.length === lastArgs.length && lastArgs.every(function (lastArg, i) {
if (calledOnce && lastThis === this && newArgs.length === lastArgs.length && lastArgs.every(function (lastArg, i) {
return isEqual(lastArg, newArgs[i]);

@@ -29,2 +31,3 @@ })) {

calledOnce = true;
lastThis = this;
lastArgs = newArgs;

@@ -31,0 +34,0 @@ lastResult = resultFn.apply(this, newArgs);

11

package.json
{
"name": "memoize-one",
"version": "1.0.0-rc.1",
"description": "A memoization library for memoizing a function with a cache size of one",
"version": "1.0.0",
"description": "A memoization library which only remembers the latest invokation",
"main": "lib/index.js",

@@ -47,3 +47,8 @@ "module": "src/index.js",

"prepublish": "yarn run build"
}
},
"keywords": [
"memoize",
"cache",
"performance"
]
}
# memoizeOne
A simple memoization library which only remembers the latest invokation
A memoization library which only remembers the latest invokation
[![Build Status](https://travis-ci.org/alexreardon/memoize-one.svg?branch=master)](https://travis-ci.org/alexreardon/memoize-one)
[![codecov](https://codecov.io/gh/alexreardon/memoize-one/branch/master/graph/badge.svg)](https://codecov.io/gh/alexreardon/memoize-one)
[![Build Status](https://travis-ci.org/alexreardon/memoize-one.svg?branch=master)](https://travis-ci.org/alexreardon/memoize-one) [![codecov](https://codecov.io/gh/alexreardon/memoize-one/branch/master/graph/badge.svg)](https://codecov.io/gh/alexreardon/memoize-one) [![dependencies](https://david-dm.org/alexreardon/memoize-one.svg)](https://david-dm.org/alexreardon/memoize-one) [![SemVer](https://img.shields.io/badge/SemVer-2.0.0-brightgreen.svg)](http://semver.org/spec/v2.0.0.html)
## DOCS: Work in progress
## Rationale

@@ -20,5 +17,4 @@

Unlike other memoization libraries, `memoizeOne` only remembers the latest arguments. No need to worry about cache busting mechanisms such as `maxAge`, `maxSize`, `exlusions` and so on which can be prone to memory leaks. `memoizeOne` simply remembers the last arguments, and if the function is next called with the same arguments then it returns the previous result.
Unlike other memoization libraries, `memoizeOne` only remembers the latest arguments and result. No need to worry about cache busting mechanisms such as `maxAge`, `maxSize`, `exlusions` and so on which can be prone to memory leaks. `memoizeOne` simply remembers the last arguments, and if the function is next called with the same arguments then it returns the previous result.
## Usage

@@ -50,2 +46,3 @@

```
[Play with this example](http://www.webpackbin.com/NkCiYkz_M)

@@ -74,3 +71,11 @@ ### Custom equality function

```
[Play with this example](http://www.webpackbin.com/NJW-tJMdf)
#### Type signature
Here is the expected [flow](http://flowtype.org) type signature for a custom equality function:
```js
type EqualityFn = (a: any, b: any) => boolean;
```
## Installation

@@ -86,13 +91,46 @@

## Other features
## `this`
### Correctly supports `this` binding
### memoizeOne correctly respects `this` control
### Custom equality function
This library takes special care to maintain, and allow control over the the `this` context for **both** the original function being memoized as well as the returned memoized function. Both the original function and the memoized function's `this` context respect [all the `this` controlling techniques](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md):
### Code health
- new bindings (`new`)
- explicit binding (`call`, `apply`, `bind`);
- implicit binding (call site: `obj.foo()`);
- default binding (`window` or `undefined` in `strict mode`);
- fat arrow binding (binding to lexical `this`)
- ignored this (pass `null` as `this` to explicit binding)
- Tested with [all JavaScript *types*](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch1.md)
- 100% code coverage
- [flow types](http://flowtype.org) for safer internal execution and type checking / auto complete for editors
- [Semantically versioning (2.0)](http://semver.org/)
### Changes to `this` is considered an argument change
Changes to the running context (`this`) of a function can result in the function returning a different value event though its arguments have stayed the same:
```js
function getA() {
return this.a;
}
const temp1 = {
a: 20,
};
const temp2 = {
a: 30,
}
getA.call(temp1); // 20
getA.call(temp2); // 30
```
Therefore, in order to prevent against unexpected results, `memoizeOne` takes into account the current execution context (`this`) of the memoized function. If `this` is different to the previous invokation then it is considered a change in argument. [further discussion](https://github.com/alexreardon/memoize-one/issues/3).
Generally this will be of no impact if you are not explicity controlling the `this` context of functions you want to memoize with [explicit binding](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#explicit-binding) or [implicit binding](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#implicit-binding). `memoizeOne` will detect when you are manipulating `this` and will then consider the `this` context as an argument. If `this` changes, it will re-execute the original function even if the arguments have not changed.
## Code health
- Tested with all built in [JavaScript types](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch1.md).
- 100% code coverage.
- [flow types](http://flowtype.org) for safer internal execution and external consumption. Also allows for editor autocompletion.
- Follows [Semantic versioning (2.0)](http://semver.org/) for safer versioning.
- Lightweight with no dependencies

@@ -7,2 +7,3 @@ // @flow

export default function (resultFn: Function, isEqual?: EqualityFn = simpleIsEqual) {
let lastThis: any;
let lastArgs: Array<any> = [];

@@ -12,4 +13,7 @@ let lastResult: any;

// breaking cache when arguments or context changes
return function (...newArgs: Array<any>) {
if (calledOnce && newArgs.length === lastArgs.length &&
if (calledOnce &&
lastThis === this &&
newArgs.length === lastArgs.length &&
lastArgs.every((lastArg, i) => isEqual(lastArg, newArgs[i]))) {

@@ -20,2 +24,3 @@ return lastResult;

calledOnce = true;
lastThis = this;
lastArgs = newArgs;

@@ -22,0 +27,0 @@ lastResult = resultFn.apply(this, newArgs);

@@ -7,24 +7,69 @@ // @flow

type Expectation = {|
args: any[],
result: any
|};
describe('memoizeOne', () => {
function getA() {
// $FlowSuppressError: allowing many values for `this`
return this.a;
}
type Input = {|
name: string,
first: Expectation,
second: Expectation
|};
describe('standard behaviour - baseline', () => {
let add;
let memoizedAdd;
describe('memoizeOne', () => {
// [JavaScript defines seven built-in types:](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch1.md)
// - null
// - undefined
// - boolean
// - number
// - string
// - object
// - symbol
beforeEach(() => {
add = sinon.spy((value1: number, value2: number): number => value1 + value2);
memoizedAdd = memoizeOne(add);
});
describe('standard behaviour', () => {
it('should return the result of a function', () => {
expect(memoizedAdd(1, 2)).to.equal(3);
});
it('should return the same result if the arguments have not changed', () => {
expect(memoizedAdd(1, 2)).to.equal(3);
expect(memoizedAdd(1, 2)).to.equal(3);
});
it('should not execute the memoized function if the arguments have not changed', () => {
memoizedAdd(1, 2);
memoizedAdd(1, 2);
expect(add.callCount).to.equal(1);
});
it('should invalidate a memoize cache if new arguments are provided', () => {
expect(memoizedAdd(1, 2)).to.equal(3);
expect(memoizedAdd(2, 2)).to.equal(4);
expect(add.callCount).to.equal(2);
});
it('should resume memoization after a cache invalidation', () => {
expect(memoizedAdd(1, 2)).to.equal(3);
expect(add.callCount).to.equal(1);
expect(memoizedAdd(2, 2)).to.equal(4);
expect(add.callCount).to.equal(2);
expect(memoizedAdd(2, 2)).to.equal(4);
expect(add.callCount).to.equal(2);
});
});
describe('standard behaviour - dynamic', () => {
type Expectation = {|
args: any[],
result: any
|};
type Input = {|
name: string,
first: Expectation,
second: Expectation
|};
// [JavaScript defines seven built-in types:](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch1.md)
// - null
// - undefined
// - boolean
// - number
// - string
// - object
// - symbol
const inputs: Input[] = [

@@ -132,3 +177,3 @@ {

inputs.forEach(({ name, first, second }) => {
describe(`dynamic type test:[${name}]`, () => {
describe(`type: [${name}]`, () => {

@@ -189,6 +234,16 @@ let spy;

describe('original function', () => {
function getA() {
return this.a;
}
it('should respect new bindings', () => {
const Foo = function (bar) {
this.bar = bar;
};
const memoized = memoizeOne(function (bar) {
return new Foo(bar);
});
const result = memoized('baz');
expect(result instanceof Foo).to.equal(true);
expect(result.bar).to.equal('baz');
});
it('should respect explicit bindings', () => {

@@ -216,5 +271,36 @@ const temp = {

it('should respect default bindings', () => {
const memoized = memoizeOne(getA);
it('should respect implicit bindings', () => {
const temp = {
a: 2,
getA,
};
const memoized = memoizeOne(function () {
return temp.getA();
});
expect(memoized()).to.equal(2);
});
it('should respect fat arrow bindings', () => {
const temp = {
a: 50,
};
function foo() {
// return an arrow function
return () => {
// `this` here is lexically adopted from `foo()`
return getA.call(this);
};
}
const bound = foo.call(temp);
const memoized = memoizeOne(bound);
expect(memoized()).to.equal(50);
});
it('should respect ignored bindings', () => {
const bound = getA.bind(null);
const memoized = memoizeOne(bound);
expect(memoized).to.throw(TypeError);

@@ -225,6 +311,16 @@ });

describe('memoized function', () => {
function getA() {
return this.a;
}
it('should respect new bindings', () => {
const memoizedGetA = memoizeOne(getA);
const Foo = function (a) {
this.a = a;
this.result = memoizedGetA.call(this);
};
const foo1 = new Foo(10);
const foo2 = new Foo(20);
expect(foo1.result).to.equal(10);
expect(foo2.result).to.equal(20);
});
it('should respect implicit bindings', () => {

@@ -272,5 +368,60 @@ const getAMemoized = memoizeOne(getA);

});
it('should respect implicit bindings', () => {
const getAMemoized = memoizeOne(getA);
const temp = {
a: 2,
getAMemoized,
};
expect(temp.getAMemoized()).to.equal(2);
});
it('should respect fat arrow bindings', () => {
const temp = {
a: 50,
};
const memoizedGetA = memoizeOne(getA);
function foo() {
// return an arrow function
return () => {
// `this` here is lexically adopted from `foo()`
return memoizedGetA.call(this);
};
}
const bound = foo.call(temp);
const memoized = memoizeOne(bound);
expect(memoized()).to.equal(50);
});
it('should respect ignored bindings', () => {
const memoized = memoizeOne(getA);
const getResult = function () {
return memoized.call(null);
};
expect(getResult).to.throw(TypeError);
});
});
});
describe('context change', () => {
it('should break the memoization cache if the execution context changes', () => {
const memoized = memoizeOne(getA);
const temp1 = {
a: 20,
getMemoizedA: memoized,
};
const temp2 = {
a: 30,
getMemoizedA: memoized,
};
expect(temp1.getMemoizedA()).to.equal(20);
expect(temp2.getMemoizedA()).to.equal(30);
});
});
describe('custom equality function', () => {

@@ -338,44 +489,3 @@ let add;

});
describe('standard behaviour', () => {
let add;
let memoizedAdd;
beforeEach(() => {
add = sinon.spy((value1: number, value2: number): number => value1 + value2);
memoizedAdd = memoizeOne(add);
});
it('should return the result of a function', () => {
expect(memoizedAdd(1, 2)).to.equal(3);
});
it('should return the same result if the arguments have not changed', () => {
expect(memoizedAdd(1, 2)).to.equal(3);
expect(memoizedAdd(1, 2)).to.equal(3);
});
it('should not execute the memoized function if the arguments have not changed', () => {
memoizedAdd(1, 2);
memoizedAdd(1, 2);
expect(add.callCount).to.equal(1);
});
it('should invalidate a memoize cache if new arguments are provided', () => {
expect(memoizedAdd(1, 2)).to.equal(3);
expect(memoizedAdd(2, 2)).to.equal(4);
expect(add.callCount).to.equal(2);
});
it('should resume memoization after a cache invalidation', () => {
expect(memoizedAdd(1, 2)).to.equal(3);
expect(add.callCount).to.equal(1);
expect(memoizedAdd(2, 2)).to.equal(4);
expect(add.callCount).to.equal(2);
expect(memoizedAdd(2, 2)).to.equal(4);
expect(add.callCount).to.equal(2);
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc