├── .babelrc ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── package.json ├── src ├── __tests__ │ └── autobind-test.js └── autobind.js └── test └── mocha.opts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "transform-class-properties" 4 | ], 5 | "presets": [ 6 | "babel-preset-es2015-loose", 7 | "babel-preset-react", 8 | "babel-preset-stage-2" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | _* 2 | !__tests__ 3 | /lib 4 | /node_modules 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "plugins": [ 4 | "babel", 5 | "flow-vars", 6 | "react" 7 | ], 8 | "env": { 9 | "browser": true, 10 | "node": true 11 | }, 12 | "globals": { 13 | "Map": false 14 | }, 15 | "rules": { 16 | "no-cond-assign": 1, // disallow assignment in conditional expressions 17 | "no-console": 0, // disallow use of console: should use nuclide-logging instead 18 | "no-constant-condition": 1, // disallow use of constant expressions in conditions 19 | "comma-dangle": [ // disallow trailing commas in object and array literals 20 | 1, "always-multiline" 21 | ], 22 | "no-control-regex": 1, // disallow control characters in regular expressions 23 | "no-debugger": 1, // disallow use of debugger 24 | "no-dupe-keys": 1, // disallow duplicate keys when creating object literals 25 | "no-dupe-args": 1, // disallow duplicate arguments in functions 26 | "no-duplicate-case": 1, // disallow a duplicate case label 27 | "no-empty": 0, // disallow empty statements 28 | "no-empty-character-class": 1, // disallow the use of empty character classes in regular expressions 29 | "no-ex-assign": 1, // disallow assigning to the exception in a catch block 30 | "no-extra-boolean-cast": 1, // disallow double-negation boolean casts in a boolean context 31 | "no-extra-semi": 1, // disallow unnecessary semicolons 32 | "no-func-assign": 1, // disallow overwriting functions written as function declarations 33 | "no-inner-declarations": 0, // disallow function or variable declarations in nested blocks 34 | "no-invalid-regexp": 1, // disallow invalid regular expression strings in the RegExp constructor 35 | "no-negated-in-lhs": 1, // disallow negation of the left operand of an in expression 36 | "no-obj-calls": 1, // disallow the use of object properties of the global object (Math and JSON) as functions 37 | "no-regex-spaces": 1, // disallow multiple spaces in a regular expression literal 38 | "no-reserved-keys": 0, // disallow reserved words being used as object literal keys 39 | "no-sparse-arrays": 1, // disallow sparse arrays 40 | "no-unreachable": 1, // disallow unreachable statements after a return, throw, continue, or break statement 41 | "use-isnan": 1, // disallow comparisons with the value NaN 42 | "valid-jsdoc": 0, // Ensure JSDoc comments are valid 43 | "valid-typeof": 1, // Ensure that the results of typeof are compared against a valid string 44 | 45 | // Best Practices (designed to prevent you from making mistakes) 46 | 47 | "block-scoped-var": 0, // treat var statements as if they were block scoped 48 | "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program 49 | "consistent-return": 0, // require return statements to either always or never specify values 50 | "curly": 1, // specify curly brace conventions for all control statements 51 | "default-case": 0, // require default case in switch statements 52 | "dot-notation": 0, // dot notation encouraged except for foreign properties that cannot be renamed (i.e., Closure Compiler rules) 53 | "eqeqeq": [1, "allow-null"], // require the use of === and !== 54 | "guard-for-in": 1, // make sure for-in loops have an if statement 55 | "no-alert": 1, // disallow the use of alert, confirm, and prompt 56 | "no-caller": 1, // disallow use of arguments.caller or arguments.callee 57 | "no-div-regex": 1, // disallow division operators explicitly at beginning of regular expression 58 | "no-else-return": 0, // disallow else after a return in an if 59 | "no-eq-null": 0, // disallow comparisons to null without a type-checking operator 60 | "no-eval": 1, // disallow use of eval() 61 | "no-extend-native": 1, // disallow adding to native types 62 | "no-extra-bind": 1, // disallow unnecessary function binding 63 | "no-fallthrough": 1, // disallow fallthrough of case statements 64 | "no-floating-decimal": 1, // disallow the use of leading or trailing decimal points in numeric literals 65 | "no-implied-eval": 1, // disallow use of eval()-like methods 66 | "no-labels": 1, // disallow use of labeled statements 67 | "no-iterator": 1, // disallow usage of __iterator__ property 68 | "no-lone-blocks": 1, // disallow unnecessary nested blocks 69 | "no-loop-func": 0, // disallow creation of functions within loops 70 | "no-multi-str": 0, // disallow use of multiline strings 71 | "no-native-reassign": 0, // disallow reassignments of native objects 72 | "no-new": 1, // disallow use of new operator when not part of the assignment or comparison 73 | "no-new-func": 1, // disallow use of new operator for Function object 74 | "no-new-wrappers": 1, // disallows creating new instances of String,Number, and Boolean 75 | "no-octal": 1, // disallow use of octal literals 76 | "no-octal-escape": 1, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; 77 | "no-proto": 1, // disallow usage of __proto__ property 78 | "no-redeclare": 1, // disallow declaring the same variable more then once 79 | "no-return-assign": 1, // disallow use of assignment in return statement 80 | "no-script-url": 1, // disallow use of javascript: urls. 81 | "no-self-compare": 1, // disallow comparisons where both sides are exactly the same 82 | "no-sequences": 1, // disallow use of comma operator 83 | "no-unused-expressions": 0, // disallow usage of expressions in statement position 84 | "no-void": 1, // disallow use of void operator 85 | "no-warning-comments": 0, // disallow usage of configurable warning terms in comments e.g. TODO or FIXME 86 | "no-with": 1, // disallow use of the with statement 87 | "radix": 1, // require use of the second argument for parseInt() 88 | "vars-on-top": 0, // requires to declare all vars on top of their containing scope 89 | "wrap-iife": 0, // require immediate function invocation to be wrapped in parentheses 90 | "yoda": 1, // require or disallow Yoda conditions 91 | "strict": 0, // this rule conflicts with 'use-babel' so we'll just disable it 92 | 93 | // Variables 94 | 95 | "no-catch-shadow": 1, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) 96 | "no-delete-var": 1, // disallow deletion of variables 97 | "no-label-var": 1, // disallow labels that share a name with a variable 98 | "no-shadow": 0, // disallow declaration of variables already declared in the outer scope 99 | "no-shadow-restricted-names": 1, // disallow shadowing of names such as arguments 100 | "no-undef": 1, // disallow undeclared variables 101 | "no-undefined": 0, // disallow use of undefined variable 102 | "no-undef-init": 0, // disallow use of undefined when initializing variables 103 | "no-unused-vars": 1, // disallow declaration of variables that are not used in the code 104 | "no-use-before-define": 0, // disallow use of variables before they are defined 105 | 106 | // Node.js 107 | 108 | "handle-callback-err": 1, // enforces error handling in callbacks 109 | "no-mixed-requires": 1, // disallow mixing regular variable and require declarations 110 | "no-new-require": 1, // disallow use of new operator with the require function 111 | "no-path-concat": 1, // disallow string concatenation with __dirname and __filename 112 | "no-process-exit": 0, // disallow process.exit() 113 | "no-restricted-modules": 1, // restrict usage of specified node modules 114 | "no-sync": 0, // disallow use of synchronous methods 115 | 116 | // React (eslint-plugin-react) 117 | 118 | "jsx-quotes": [1, "prefer-double"], // not 119 | "react/jsx-curly-spacing": [ // Enforce or disallow spaces inside of curly braces in JSX attributes 120 | 1, "never" 121 | ], 122 | "react/jsx-no-undef": 1, // Disallow undeclared variables in JSX 123 | "react/jsx-uses-react": 1, // Prevent React to be incorrectly marked as unused 124 | "react/jsx-uses-vars": 1, // Prevent variables used in JSX to be incorrectly marked as unused 125 | "react/no-unknown-property": 1, // Prevent usage of unknown DOM property 126 | "react/prop-types": 1, // Prevent missing props validation in a React component definition 127 | "react/react-in-jsx-scope": 2, // Prevent missing React when using JSX 128 | 129 | // Stylistic (these rules are purely matters of style and are quite subjective) 130 | 131 | "key-spacing": 1, // require space after colon `{a: 1}` 132 | "comma-spacing": 1, // require space after comma `var a, b;` 133 | "no-multi-spaces": 1, // don't allow more spaces than necessary 134 | "brace-style": [ // enforce one true brace style 135 | 1, "1tbs", { 136 | "allowSingleLine": false 137 | } 138 | ], 139 | "camelcase": [ // require camel case names 140 | 1, {"properties": "never"} 141 | ], 142 | "consistent-this": [1, "self"], // enforces consistent naming when capturing the current execution context 143 | "eol-last": 1, // enforce newline at the end of file, with no multiple empty lines 144 | "func-names": 0, // require function expressions to have a name 145 | "func-style": 0, // enforces use of function declarations or expressions 146 | "new-cap": 0, // require a capital letter for constructors 147 | "new-parens": 1, // disallow the omission of parentheses when invoking a constructor with no arguments 148 | "no-nested-ternary": 0, // disallow nested ternary expressions 149 | "no-array-constructor": 1, // disallow use of the Array constructor 150 | "no-lonely-if": 0, // disallow if as the only statement in an else block 151 | "no-new-object": 1, // disallow use of the Object constructor 152 | "no-spaced-func": 1, // disallow space between function identifier and application 153 | "semi-spacing": 1, // disallow space before semicolon 154 | "no-ternary": 0, // disallow the use of ternary operators 155 | "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines 156 | "no-underscore-dangle": 0, // disallow dangling underscores in identifiers 157 | "no-extra-parens": [1, "functions"], // disallow wrapping of non-IIFE statements in parens 158 | "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation 159 | "indent": [1, 2, {"SwitchCase": 1}], // indentation should be two spaces 160 | "quotes": [ // enforce single quotes, allow double to avoid escaping ("don't escape" instead of 'don\'t escape') 161 | 1, "single", "avoid-escape" 162 | ], 163 | "quote-props": [1, "as-needed"], // require quotes around object literal property names 164 | "semi": 1, // require or disallow use of semicolons instead of ASI 165 | "sort-vars": 0, // sort variables within the same declaration block 166 | "keyword-spacing": 1, // require a space around certain keywords 167 | "space-before-blocks": 1, // require a space before blocks 168 | "space-before-function-paren": [ // disallow a space before function parenthesis 169 | 1, "never" 170 | ], 171 | "object-curly-spacing": [ // disallow spaces inside of curly braces in object literals 172 | 1, "never" 173 | ], 174 | "array-bracket-spacing": [ // disallow spaces inside of curly braces in array literals 175 | 1, "never" 176 | ], 177 | "space-in-parens": 1, // require or disallow spaces inside parentheses 178 | "space-infix-ops": 1, // require spaces around operators 179 | "space-unary-ops": 1, // require a space around word operators such as typeof 180 | "max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested 181 | "one-var": [1, "never"], // allow just one var statement per function 182 | "wrap-regex": 0, // require regex literals to be wrapped in parentheses 183 | 184 | // ECMAScript 6/7 (2015 and above) 185 | "arrow-parens": 1, // require parens in arrow function arguments 186 | "arrow-spacing": 1, // require space before/after arrow function's arrow (fixable) 187 | "constructor-super": 1, // verify calls of super() in constructors 188 | "generator-star-spacing": 0, // enforce spacing around the * in generator functions (fixable) 189 | "no-class-assign": 1, // disallow modifying variables of class declarations 190 | "no-const-assign": 1, // disallow modifying variables that are declared using const 191 | "no-dupe-class-members": 1, // disallow duplicate name in class members 192 | "no-this-before-super": 1, // disallow use of this/super before calling super() in constructors. 193 | "no-var": 0, // require let or const instead of var 194 | "object-shorthand": 0, // require method and property shorthand syntax for object literals 195 | "prefer-arrow-callback": 1, // suggest using arrow functions as callbacks 196 | "prefer-const": 0, // suggest using const declaration for variables that are never modified after declared 197 | "prefer-reflect": 0, // suggest using Reflect methods where applicable 198 | "prefer-spread": 0, // suggest using the spread operator instead of .apply(). 199 | "prefer-template": 0, // suggest using template literals instead of strings concatenation 200 | "require-yield": 0, // disallow generator functions that do not have yield 201 | "babel/no-await-in-loop": 1, // async inside a loop will run operations in serial, when often the desired behavior is to do do in parallel 202 | 203 | // Legacy (included for compatibility with JSHint and JSLint. While the names of the rules may not match up with the JSHint/JSLint counterpart, the functionality is the same) 204 | 205 | "max-depth": 0, // specify the maximum depth that blocks can be nested 206 | //"max-len": [ // specify the maximum length of a line in your program [warning level, max line length, number of characters to treat a tab as] 207 | // 1, 100, 2, { 208 | // "ignoreUrls": true, 209 | // "ignorePattern": "^\\s*(import\\s[^{]+from|(var|const|let)\\s[^{]+=\\s*require\\s*\\()" 210 | // } 211 | //], 212 | "max-params": 0, // limits the number of parameters that can be used in the function declaration. 213 | "max-statements": 0, // specify the maximum number of statement allowed in a function 214 | "no-bitwise": 0, // disallow use of bitwise operators 215 | "no-plusplus": 0 // disallow use of unary operators, ++ and -- 216 | 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*node_modules/babel.* 3 | .*node_modules/fbjs.* 4 | 5 | [include] 6 | 7 | [libs] 8 | 9 | [options] 10 | esproposal.class_static_fields=enable 11 | suppress_type=$FlowIssue 12 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _* 2 | !__tests__ 3 | /lib 4 | /node_modules 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .babelrc 2 | .eslintignore 3 | .eslintrc 4 | .flowconfig 5 | .travis.yml 6 | _* 7 | /src 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Simon Sturmer 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Method auto-bind for ES6 (ES2015) classes 2 | 3 | This package provides a single function, `autobind`, for use within a constructor to bind all methods to the instance itself. 4 | 5 | For example, this allows us to pass a method to an event handler `element.addEventListener('click', this.onClick)` and be sure the `onClick` method will always be called with the right context. 6 | 7 | Note: This has some specific logic for React, but could be used in any project. 8 | 9 | ## Installation: 10 | 11 | `npm install --save class-autobind` 12 | 13 | ## Usage: 14 | 15 | ```js 16 | import autobind from 'class-autobind'; 17 | 18 | class MyComponent extends React.Component { 19 | constructor() { 20 | super(...arguments); 21 | autobind(this); 22 | } 23 | render() { 24 | return ; 25 | } 26 | onClick() { 27 | console.log('Button Clicked'); 28 | } 29 | } 30 | ``` 31 | 32 | ## Advanced Usage: 33 | 34 | If your component will possibly be subclassed (you really should not do this, but some third-party libraries like [react-css-modules](https://npmjs.com/package/react-css-modules) do so) then you will need to specify which prototype will be the source of methods that are to be automatically bound. 35 | 36 | ```js 37 | import autobind from 'class-autobind'; 38 | 39 | class MyComponent extends React.Component { 40 | constructor() { 41 | super(...arguments); 42 | autobind(this, MyComponent.prototype); // Note the second parameter. 43 | } 44 | render() { 45 | /* ... */ 46 | } 47 | } 48 | 49 | class MySubClassedComponent extends MyComponent { 50 | /* This is probably a very bad idea. */ 51 | } 52 | ``` 53 | 54 | ## License 55 | 56 | This software is [BSD Licensed](/LICENSE). 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "class-autobind", 3 | "version": "0.1.4", 4 | "description": "Method auto-bind for ES6 (ES2015) classes", 5 | "main": "lib/autobind.js", 6 | "scripts": { 7 | "build": "rimraf lib && babel src --ignore '_*' --out-dir lib", 8 | "prepublish": "npm run build", 9 | "lint": "eslint --max-warnings 0 .", 10 | "typecheck": "flow", 11 | "test-src": "mocha src/__tests__/*.js src/**/__tests__/*.js", 12 | "test": "npm run lint && npm run typecheck && npm run test-src" 13 | }, 14 | "dependencies": {}, 15 | "devDependencies": { 16 | "babel-cli": "^6.10.1", 17 | "babel-core": "^6.9.1", 18 | "babel-eslint": "^6.0.4", 19 | "babel-plugin-transform-class-properties": "^6.9.1", 20 | "babel-preset-es2015": "^6.9.0", 21 | "babel-preset-es2015-loose": "^7.0.0", 22 | "babel-preset-react": "^6.5.0", 23 | "babel-preset-stage-2": "^6.5.0", 24 | "eslint": "^2.12.0", 25 | "eslint-plugin-babel": "^3.2.0", 26 | "eslint-plugin-flow-vars": "^0.4.0", 27 | "eslint-plugin-react": "^5.1.1", 28 | "expect": "^1.20.1", 29 | "flow-bin": "^0.27.0", 30 | "mocha": "^2.5.3", 31 | "rimraf": "^2.5.2" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git+ssh://git@github.com/kodefox/class-autobind.git" 36 | }, 37 | "keywords": [ 38 | "react", 39 | "autobind", 40 | "es6-class" 41 | ], 42 | "author": "team@kodefox.com", 43 | "license": "ISC", 44 | "bugs": { 45 | "url": "https://github.com/kodefox/class-autobind/issues" 46 | }, 47 | "homepage": "https://github.com/kodefox/class-autobind#readme" 48 | } 49 | -------------------------------------------------------------------------------- /src/__tests__/autobind-test.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | const {describe, it} = global; 3 | import expect from 'expect'; 4 | import autobind from '../autobind'; 5 | 6 | const invoke = (fn) => fn(); 7 | 8 | describe('autobind', () => { 9 | it('should bind methods', () => { 10 | class Foo { 11 | constructor() { 12 | autobind(this); 13 | } 14 | getThis() { 15 | return this; 16 | } 17 | } 18 | let foo = new Foo(); 19 | expect(foo.getThis).toNotBe(Foo.prototype.getThis); 20 | expect(invoke(foo.getThis)).toBe(foo); 21 | }); 22 | 23 | it('should NOT bind excluded methods', () => { 24 | class Foo { 25 | constructor() { 26 | autobind(this); 27 | } 28 | render() { 29 | return this; 30 | } 31 | } 32 | let foo = new Foo(); 33 | expect(foo.render).toBe(Foo.prototype.render); 34 | expect(invoke(foo.render)).toBe(undefined); 35 | }); 36 | 37 | it('should NOT accept optional second param', () => { 38 | class Foo { 39 | constructor() { 40 | // When called from a subclass via super(), we cannot auto-detect which 41 | // prototype we should use as a source for autobind. 42 | autobind(this, Foo.prototype); 43 | } 44 | getThis() { 45 | return this; 46 | } 47 | } 48 | class Bar extends Foo {} 49 | let bar = new Bar(); 50 | expect(bar.getThis).toNotBe(Foo.prototype.getThis); 51 | expect(invoke(bar.getThis)).toBe(bar); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /src/autobind.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | // The following React methods should not be automatically bound. 4 | const REACT_EXCLUDE_METHODS = { 5 | getChildContext: true, 6 | render: true, 7 | componentWillMount: true, 8 | componentDidMount: true, 9 | componentWillReceiveProps: true, 10 | shouldComponentUpdate: true, 11 | componentWillUpdate: true, 12 | componentDidUpdate: true, 13 | componentWillUnmount: true, 14 | }; 15 | 16 | function isExcluded(methodName) { 17 | return (REACT_EXCLUDE_METHODS[methodName] === true); 18 | } 19 | 20 | function isFunction(item) { 21 | return (typeof item === 'function'); 22 | } 23 | 24 | export default function autobind(instance: Object, proto: ?Object) { 25 | if (proto == null) { 26 | proto = Object.getPrototypeOf(instance); 27 | } 28 | let propertyNames = Object.getOwnPropertyNames(proto); 29 | for (let name of propertyNames) { 30 | let value = proto[name]; 31 | if (isFunction(value) && !isExcluded(name)) { 32 | instance[name] = proto[name].bind(instance); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers js:babel-core/register 2 | --------------------------------------------------------------------------------