For Magento 1, check out my post here: Four ways to edit Magento's Javascript.
A while ago I wrote a reasonably popular article that simply listed out the different ways you can modify Magento's Javascript. Here we go again, but for Magento 2.
Theme overwrite
Of course, the easiest way to modify Javascript is to copy the file over in your own theme, and make your changes there.
For example, copying the following file:
vendor/magento/module-swatches/view/frontend/web/js/swatch-renderer.js
to
app/design/frontend/{Namespace}/{Theme}/Magento_Swatches/web/js/swatch-renderer.js
If going down this path, I encourage (read force) my team to always Git commit the original file before making any changes - it makes code reviews much easier; you can see what was part of the original Magento codebase, and what was edited. I think it's good practice to do this with any overrides (especially template files).
RequireJS override
Alan Storm has already written thoroughly on this subject so I won't explain it here. In short, the identifier that is used when requesting a dependency in your module definition, requirejs, x-magento-init script, or data-mage-init attribute can be pointed to a different asset. With a file named requirejs-config.js
.
Example:
// File: app/code/{Namespace}/{Theme}/view/frontend/requirejs-config.js
var config = {
map: {
'*': {
Magento_Swatch/js/swatch-renderer.js: '{Namespace}_{Module}/js/{filename}',
}
}
};
Above forces the dependency to be loaded from your module instead.
This approach might make it hard to debug as developers don't expect an explicit path to be rewritten like that. However, if you're working within an extension as opposed to a theme (if you're an extension developer for example), this seems to be the only approach. If you're customising a theme, it's better to take the first approach, and override the JS file in your theme.
Magento 2 Mixins
Arguably the best way to modify JS is through a concept that Magento calls "Mixins".
It's a system that works with RequireJS, but is unique to Magento 2. It allows you to modify any dependency when it is requested, and doesn't require you to overwrite the file, allowing for easier Magento upgrades.
Let's continue with the swatch-renderer.js
example. Add the following contents to your requirejs-config.js
file:
// File: app/code/{Namespace}/{Theme}/view/frontend/requirejs-config.js
var config = {
'config':{
'mixins': {
'Magento_Swatches/js/swatch-renderer': {
'{Namespace}_{Module}/js/{filename}':true
}
}
}
};
And to your "hook" file:
// File: app/code/{Namespace}/{Theme}/view/frontend/web/js/{filename}.js
define([], function(){
'use strict';
return function(swatchRenderer) {
// Do something with swatchRenderer
return swatchRenderer
};
});
Now, when the swatch-renderer
dependency is requested, it goes through your custom file first. This allows you to modify behaviour through plugin-like calls. It does require some basic knowledge on JS closures, objects and how they work; hooking into a jQuery widget is different than a Magento uiComponent.
Again, Alan Storm has a great write-up of mixins, so go read his article on Magento 2 Mixins, and of course this article on modifying jQuery widgets through mixins.
I'm not too sure how I feel about mixins - in theory it's a great way to modify JS behaviour. However, I've noticed it sometimes doesn't load in Firefox or on mobile on a first page-hit and as such it doesn't feel very robust (which could entirely be my own fault). However, it is the way to go for customisations.