TL;DR

Let's say we have three objects chained together:

var objectOne   = { a: 10 };
var objectTwo   = Object.create(objectOne);
var objectThree = Object.create(objectTwo);

console.log(objectOne.a);   // 10
console.log(objectTwo.a);   // 10
console.log(objectThree.a); // 10

console.log(objectOne.hasOwnProperty("a"));   // true
console.log(objectTwo.hasOwnProperty("a"));   // false
console.log(objectThree.hasOwnProperty("a")); // false

Starting with something simple, we try to assign a value to the a property of objectThree.

var objectOne   = { a: 10 };
var objectTwo   = Object.create(objectOne);
var objectThree = Object.create(objectTwo);

objectThree.a = 20;

console.log(objectOne.a);   // 10
console.log(objectTwo.a);   // 10
console.log(objectThree.a); // 20

console.log(objectOne.hasOwnProperty("a"));   // true
console.log(objectTwo.hasOwnProperty("a"));   // false
console.log(objectThree.hasOwnProperty("a")); // true

So we got the rule:

If the target object does not have the specified property of the assignment, that property will be created on the target object. Whether you can find the property on the phototype chain does not count.

Spoiler: This rule is wrong.

Let's consider another scenario:

var objectOne = {};
Object.defineProperty(objectOne, "a", {
    value: 10,
    writable: false
});
var objectTwo   = Object.create(objectOne);
var objectThree = Object.create(objectTwo);

objectThree.a = 20;

console.log(objectOne.a);   // 10
console.log(objectTwo.a);   // 10
console.log(objectThree.a); // 10, hmm...

console.log(objectOne.hasOwnProperty("a"));   // true
console.log(objectTwo.hasOwnProperty("a"));   // false
console.log(objectThree.hasOwnProperty("a")); // false

Oops.

No property was created on objectThree. The only difference is that this time there is a read only property with the same name up on the prototype chain.

Can we gather that shadowing read only properties is not allowed? What if there is a read only property with the same name up on the chain, but there is also a normal property (with the same name, obviously) in-between?

var objectOne = {};
Object.defineProperty(objectOne, "a", {
    value: 10,
    writable: false
});
var objectTwo   = Object.create(objectOne);
// ...and the "can't shadow read only properties" rule
// can be bypassed with the Object.defineProperty function
Object.defineProperty(objectTwo, "a", {
    value: 20,
    writable: true
});
var objectThree = Object.create(objectTwo);

objectThree.a = 30;

console.log(objectOne.a);   // 10
console.log(objectTwo.a);   // 20
console.log(objectThree.a); // 30

console.log(objectOne.hasOwnProperty("a"));   // true
console.log(objectTwo.hasOwnProperty("a"));   // true
console.log(objectThree.hasOwnProperty("a")); // true

So here we go, a normal property in-between did shadow the read only property up on the chain and made the assignment possible.

Now let's try summarise the rule again:

Upon a property assignment, if the target object does not yet have that property, the JavaScript engine searches the prototype chain and stops at the first property it finds. If this property is read only, then the assignment fails. Otherwise a new property is created on the target object and is assigned the given value.

But is that all? Apparently not. What if we put a computed property higher on the prototype chain? Something like this:

var objectOne = {
    get a() {
        return this._a;
    },
    set a(value) {
        this._a = value;
    }
};
objectOne.a = 10;

var objectTwo   = Object.create(objectOne);
var objectThree = Object.create(objectTwo);

objectThree.a = 30;

console.log(objectOne.a);   // 10
console.log(objectTwo.a);   // 10
console.log(objectThree.a); // 30  

console.log(objectOne.hasOwnProperty("a"));   // true
console.log(objectTwo.hasOwnProperty("a"));   // false
console.log(objectThree.hasOwnProperty("a")); // false! No property created.

A setter higher on the prototype chain will take over and get called. And it will be invoked in the context of the target object (aka this will be the target object).

Again we try to put a normal property in-between.

var objectOne = {
    get a() {
        return this._a;
    },
    set a(value) {
        this._a = value;
    }
};
objectOne.a = 10;

var objectTwo   = Object.create(objectOne);
Object.defineProperty(objectTwo, "a", {
    value: 20,
    writable: true
});
var objectThree = Object.create(objectTwo);

objectThree.a = 30;

console.log(objectOne.a);   // 10
console.log(objectTwo.a);   // 20
console.log(objectThree.a); // 30  

console.log(objectOne.hasOwnProperty("a"));   // true
console.log(objectTwo.hasOwnProperty("a"));   // true
console.log(objectThree.hasOwnProperty("a")); // true

As expected, the in-between normal property "blocked" the prototype chain search and allowed the value assignment to procceed.

So we finally have the rules:

When an assignment is made to a property that the target object does not already have, the JavaScript engine searches the target object's prototype chain for a property with the same name.

  1. If no such property is found in the prototype chain, the specified property is created on the target object and is assigned with the value given.

  2. If a property is found in the prototype chain, the engine stops and will not search further. Any other property with the same name higher up on the prototype chain will not affect this process.

    2.1 If the found property is a "normal" data accessor, the specified property is created on the target object and is assigned with the value given.

    2.2 If the found property is read-only (aka writable = false), the assignment is failed.

    2.3 If the found property is a computed property with a getter function and a setter function, the setter function is invoked in the context of the target object.