How do I update the state of lightswitches reliably using checkboxes on a frontend user profile form?
While both lightswitches and checkboxes represent an on/off state, they are quite different. Standard browser behavior is designed to only post checkbox data if the checkbox is checked, otherwise it is ignored. In PHP, when processing the form, you would want to check whether the post-data is present or not; if it's present then the value is checked; otherwise it's unchecked.

Although I haven't reviewed how craft processes checkbox form submissions I would assume that it also performs this check behind the scenes, and converts the value to a 1 or 0 to store the data. However, being a lightswitch, craft probably does not perform this check, so if the field is missing from the post-data (which it would be if unchecked), then craft simply ignores it, as not being part of the form to begin with.

There may be a simpler method built in that I'm not aware of, but one solution would be to create a hidden field with your lightswitch handle, and then set that field using javascript when the user checks or unchecks the checkbox, much the way that craft does.

Here is the standard lightswitch html for example so you can see how craft sets this up:

&lt;div class="lightswitch" tabindex="0"&gt;
    &lt;div class="lightswitch-container" style="margin-left: -11px;"&gt;
        &lt;div class="label on"&gt;&lt;/div&gt;
        &lt;div class="handle"&gt;&lt;/div&gt;
        &lt;div class="label off"&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;input type="hidden" name="fields[switchHandle]" value=""&gt;
&lt;/div&gt;


You would want something like this:

&lt;div class="checkbox"&gt;
    &lt;label&gt;
        &lt;input type="checkbox" class="js-checkbox" {% if currentUser.memberShowEmailLink %}checked="checked"{% endif %}&gt;
        Show a link to contact me via email in the member directory?
    &lt;/label&gt;
    &lt;input type="hidden" class="js-lightbox" name="fields[memberShowEmailLink]" value="{% if currentUser.memberShowEmailLink %}1{% endif %}"&gt;
&lt;/div&gt;


Then in javascript:

$('.js-checkbox').on('change', function(event) {
    if($(this).prop('checked')) {
        $('.js-lightbox').val(1);
    } else {
        $('.js-lightbox').val(0);
    }
});


Note: if you leave off the 'name' attribute the dummy checkbox should not get submitted.