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

class-autobind-decorator

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

class-autobind-decorator - npm Package Compare versions

Comparing version 2.0.0 to 2.1.0

45

build/index.js

@@ -26,2 +26,5 @@ 'use strict';

* @param {String[]} [options.methodsToIgnore] - names of methods to skip auto-binding
* @param {boolean} [options.dontOptimize] - if truthy, turns off the decorator's default
* optimization behavior, which is to define the bound method directly on the class instance
* in order to prevent lookups and re-binding on every access
* @returns {*}

@@ -45,3 +48,6 @@ */

* @param {Object} [options] - optional options
* @param {String[]} [options.methodsToIgnore] - names of methods to skip auto-binding
* @param {string[]} [options.methodsToIgnore] - names of methods to skip auto-binding
* @param {boolean} [options.dontOptimize] - if truthy, turns off the decorator's default
* optimization behavior, which is to define the bound method directly on the class instance
* in order to prevent lookups and re-binding on every access
*/

@@ -57,4 +63,7 @@ function autoBindMethodsDecorator(target) {

var _options$methodsToIgn = options.methodsToIgnore,
methodsToIgnore = _options$methodsToIgn === undefined ? [] : _options$methodsToIgn;
methodsToIgnore = _options$methodsToIgn === undefined ? [] : _options$methodsToIgn,
_options$dontOptimize = options.dontOptimize,
dontOptimize = _options$dontOptimize === undefined ? false : _options$dontOptimize;
var ownProps = typeof Object.getOwnPropertySymbols === 'function' ? Object.getOwnPropertyNames(prototype).concat(Object.getOwnPropertySymbols(prototype)) : Object.getOwnPropertyNames(prototype);

@@ -70,6 +79,7 @@

var propDescriptor = Object.getOwnPropertyDescriptor(prototype, ownPropIdentifier);
var value = propDescriptor.value;
var value = propDescriptor.value,
configurable = propDescriptor.configurable;
if (typeof value !== 'function' || !propDescriptor.configurable) {
if (typeof value !== 'function' || !configurable) {
// We can only do our work with configurable functions, so bail early here.

@@ -79,16 +89,21 @@ return;

var boundMethod = void 0;
Object.defineProperty(prototype, ownPropIdentifier, {
get: function get() {
if (!boundMethod) {
if (!(this instanceof target)) {
// We don't want to bind to something that isn't an instance of the constructor in the rare
// case where the property is read by some means other than an instance *before* it has been
// bound (e.g., if something checks whether the method exists via the prototype, as in
// `someConstructor.prototype.someProp`), so we just return the unbound method in that case.
return value;
}
if (this.hasOwnProperty(ownPropIdentifier)) {
// Don't bind the prototype's method to the prototype, or we can't re-bind it.
return value;
}
boundMethod = value.bind(this);
var boundMethod = value.bind(this);
if (!dontOptimize) {
var _configurable = propDescriptor.configurable,
writable = propDescriptor.writable;
Object.defineProperty(this, ownPropIdentifier, {
value: boundMethod,
configurable: _configurable,
writable: writable
});
}

@@ -95,0 +110,0 @@

# CHANGELOG
## v2.1.0 (11/18/2016)
- Add optimizations (add methods to instances and unnecessary re-binding)
- Add `dontOptimize` flag for overridding optimizations if necessary
- Actually make this thing work for classes with more than one instance
(whoops!). This is the *real* fix for issue #2.
## v2.0.0 (11/16/2016)

@@ -4,0 +11,0 @@

{
"name": "class-autobind-decorator",
"version": "2.0.0",
"version": "2.1.0",
"description": "A small framework-agnostic utility for auto-binding \"class\" methods to instances (with customization options) using either \"legacy\" decorator syntax or plain old ES5 (without needing ES2015+ polyfills).",

@@ -5,0 +5,0 @@ "main": "build/index.js",

@@ -10,11 +10,12 @@ # class-autobind-decorator

- Usable with ES6 [React](https://facebook.github.io/react/) classes,
- Usable with ES6+ [React](https://facebook.github.io/react/) and [Preact](https://preactjs.com) classes,
but also elsewhere
- Built version is fully ES5-compatible, requiring no ES6+ polyfills
- Supports class methods that have ES6 Symbols as keys
- Accepts passed-in options (see examples below), allowing the user to
- Accepts configuration options (see examples below), allowing the user to
specify the methods that should or should not be bound (supporting both
named methods and methods with Symbols as keys in this case, as well)
named methods and methods with Symbols as keys in this case, as well), and
to specify whether or not to perform certain optimizations
- Does not attempt to redefine methods marked as non-configurable
(actually checks for configurability first!)
(checks for configurability first!)
- Conforms to [the ES6+ "legacy" decorator pattern](https://babeljs.io/docs/plugins/transform-decorators/),

@@ -24,3 +25,3 @@ and hence is usable as an ES6+ legacy decorator

or as a configured decorator (`@autoBindMethods(options)`)
- Extensively documented and tested
- Documented and tested

@@ -131,5 +132,15 @@ ## Installation

## Configuration Options
**methodsToIgnore**: An array of method names that should not be bound if found on the prototype.
See the above examples for usage.
**dontOptimize**: The default behavior of this decorator is to only bind methods to instances once,
and, from that point onward, to store the bound method on the instance itself. You can override
this behavior by setting `dontOptimize` to true.
## Building
Clone the repository, then, in the main (top-level) repo directory:
Clone the repository, run `npm install`, then, in the main (top-level) repo
directory:

@@ -141,3 +152,3 @@ ```js

Compiled code will be placed in the `./build` directory. You can also
download it directly from this repository.
download compiled code directly from this repository.

@@ -162,7 +173,8 @@ ## Running Tests

aware of either hard-code React-specific stuff, or don't check whether
properties are configurable before trying to redefine them, or can't be
used as both "bare" (unconfigured) decorators and configured decorators,
and things like that -- no hate, though). I also just wanted an
properties are configurable before trying to redefine them, or inadvertently
bind to non-instance objects when methods are accessed first via the prototype,
or can't be used as both "bare" (unconfigured) decorators and configured
decorators, and things like that -- no hate, though!). I also just wanted an
opportunity to work more directly with decorators, so I used it as a
learning experience.
learning experience. :)

@@ -169,0 +181,0 @@ ## License

@@ -17,2 +17,5 @@ /**

* @param {String[]} [options.methodsToIgnore] - names of methods to skip auto-binding
* @param {boolean} [options.dontOptimize] - if truthy, turns off the decorator's default
* optimization behavior, which is to define the bound method directly on the class instance
* in order to prevent lookups and re-binding on every access
* @returns {*}

@@ -36,3 +39,6 @@ */

* @param {Object} [options] - optional options
* @param {String[]} [options.methodsToIgnore] - names of methods to skip auto-binding
* @param {string[]} [options.methodsToIgnore] - names of methods to skip auto-binding
* @param {boolean} [options.dontOptimize] - if truthy, turns off the decorator's default
* optimization behavior, which is to define the bound method directly on the class instance
* in order to prevent lookups and re-binding on every access
*/

@@ -48,3 +54,4 @@ function autoBindMethodsDecorator(target, options = {}) {

const { prototype } = target;
const { methodsToIgnore = [] } = options;
const { methodsToIgnore = [], dontOptimize = false } = options;
let ownProps = typeof Object.getOwnPropertySymbols === 'function' ?

@@ -60,5 +67,5 @@ Object.getOwnPropertyNames(prototype).concat(Object.getOwnPropertySymbols(prototype)) :

const propDescriptor = Object.getOwnPropertyDescriptor(prototype, ownPropIdentifier);
const { value } = propDescriptor;
const { value, configurable } = propDescriptor;
if (typeof value !== 'function' || !propDescriptor.configurable) {
if (typeof value !== 'function' || !configurable) {
// We can only do our work with configurable functions, so bail early here.

@@ -68,16 +75,19 @@ return;

let boundMethod;
Object.defineProperty(prototype, ownPropIdentifier, {
get() {
if (!boundMethod) {
if (!(this instanceof target)) {
// We don't want to bind to something that isn't an instance of the constructor in the rare
// case where the property is read by some means other than an instance *before* it has been
// bound (e.g., if something checks whether the method exists via the prototype, as in
// `someConstructor.prototype.someProp`), so we just return the unbound method in that case.
return value;
}
if (this.hasOwnProperty(ownPropIdentifier)) {
// Don't bind the prototype's method to the prototype, or we can't re-bind it.
return value;
}
boundMethod = value.bind(this);
const boundMethod = value.bind(this);
if (!dontOptimize) {
const { configurable, writable } = propDescriptor;
Object.defineProperty(this, ownPropIdentifier, {
value: boundMethod,
configurable,
writable
});
}

@@ -84,0 +94,0 @@

@@ -107,2 +107,29 @@ import 'babel-polyfill';

});
describe('when the options specify dontOptimize', function () {
it('should not alter the instance and should re-bind on every access', function () {
const [MyFirstClass] = getClasses();
const customAutoBinder = autoBindMethods({ dontOptimize: true });
customAutoBinder(MyFirstClass);
const myFirstInstance = new MyFirstClass();
const a = myFirstInstance.testMethodOne;
const b = myFirstInstance.testMethodOne;
expect(a).not.to.equal(b);
expect(myFirstInstance.hasOwnProperty('testMethodOne')).to.equal(false);
});
});
describe('when the options do not specify dontOptimize', function () {
it('should alter the instance and not re-bind on every access', function () {
const [, MySecondClass] = getClasses();
const myFirstInstance = new MySecondClass();
const a = myFirstInstance.testMethodOne;
const b = myFirstInstance.testMethodOne;
expect(a).to.equal(b);
expect(myFirstInstance.hasOwnProperty('testMethodOne')).to.equal(true);
})
})
});

@@ -131,5 +158,6 @@

describe('when a method is accessed by something other than the instance', function () {
it('should not bind the method, but should only bind when access *is* by the instance', function () {
const [ , MySecondClass, ] = getClasses();
describe('when a method is accessed via the prototype', function () {
it('should not bind the method to the prototype', function () {
const [ , MySecondClass] = getClasses();
MySecondClass.prototype.testMethodOne; // `get` via prototype, not instance, should not bind

@@ -143,2 +171,19 @@ const myInstance = new MySecondClass();

});
describe('when applied to a class that has multiple instances', function () {
it('should bind the method to each individual instance', function () {
const [ , MySecondClass] = getClasses();
const myFirstInstance = new MySecondClass();
const mySecondInstance = new MySecondClass();
let { testMethodOne } = myFirstInstance;
expect(testMethodOne(myFirstInstance)).to.equal(true);
expect(testMethodOne(mySecondInstance)).to.equal(false);
testMethodOne = mySecondInstance.testMethodOne;
expect(testMethodOne(mySecondInstance)).to.equal(true);
});
});
});
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