Object.create()

Jednym z kolejnych sposobów na tworzenie obiektów jest użycie funkcji Object.create(), która służy do tworzenia nowych obiektów.

Jako pierwszy parametr przyjmuje ona obiekt, który stanie się prototypem nowo tworzonego obiektu:


const car = { //to będzie nasz prototyp
    drive() {
        console.log(this.name + " jadę");
    },
    refuel() {
        console.log(this.name + " tankuję");
    },
    stop() {
        console.log(this.name + " zatrzymuję się");
    }
}

const c1 = Object.create(car);
c1.name = "Samochód 1";
c1.drive(); //Samochód 1 jadę

const c2 = Object.create(car);
c2.name = "Samochód 2";
c2.drive(); //Samochód 2 jadę

console.log(c1);
console.log(c2);

Drugim opcjonalnym parametrem Object.create() jest obiekt, w którym możemy ustawić właściwości nowo tworzonego obiektu:


const c1 = Object.create(car, {
    name: {
        value: 'Maluch'
    },
    km: {
        value: 0
    }
}

Deskryptory właściwości

W odróżnieniu od klasycznego ustawiania właściwości (np. c1.name = "Maluch"), przy zastosowaniu Object.create() mamy tutaj dodatkowe możliwości, ponieważ każdą tak tworzoną właściwość możemy opisać poprzez dodatkowe cechy:

enumerableczy wartość ma być widoczna przy pętlach iteracyjnych (np. for in, for of itp.) - np. właściwość length nie jest enumerable
writable czy zmienna może być zmieniana np. car.name = "Fiat"
configurable czy zmienna może mieć później zmieniane cechy - np. czy możemy później zmienić ją na nie enumerable, lub czy będziemy mogli ją usunąć poprzez delete)
valuewartość tej właściwości. Nie można tego używać wraz z get/set
gettak zwany getter - funkcja wywoływana gdy pobieramy daną wartość
settak zwany setter - funkcja wywoływana gdy ustawiamy daną wartość

const user = Object.create({}, {
    name : {
        value : "Marcin",
        configurable : true,
        writable : true
    },
    surname : {
        value : "Nowak",
        configurable : true,
        writable : true
    },
    status : {
        value : "single",
        configurable : true,
        writable : false
    }
});

user.status = "married";
console.log(user.status); //"single"

Podobnie jak w przypadku poprzedniego rozdziału możemy to też połączyć z geterami i seterami:


const user = Object.create({}, {
    name : {
        value : "Marcin",
        configurable : true,
        writable : true
    },
    surname : {
        value : "Nowak",
        configurable : true,
        writable : true
    },
    status : {
        value : "single",
        configurable : true,
        writable : false
    }
    fullName : {
        get : function() {
            return this.name + "  " + this.surname;
        },
        set : function(full) {
            const words = full.split(" ");
            this.name = words[0];
            this.surname = words[1];

        }
    }
});

user.fullName = "Karol Nowak";
console.log(user.name); //Karol
console.log(user.surname); //Nowak

defineProperty()

W powyższym kodzie stworzyliśmy obiekt, od razu przy tym ustawiając jego właściwości. Możemy też takie właściwości dodawać poza ciałem obiektu za pomocą defineProperty():


const ob = {
    _name : "Piotr",
    _height : 180
};

Object.defineProperty(ob, "name", {
    set : function(newName) {
        this._name = newName;
    },
    get : function() {
        return this._name[0].toUpperCase() + this._name.substr(1).toLowerCase();
    }
});

Object.defineProperty(ob, "height", {
    get : function(height) {
        return this._height;
    },
    set : function(height) {
        this._height = height + 'cm';
    }
});

Object.defineProperty(ob, "gender", {
    writable : false,
    value : "male"
});

console.log(ob.name); //Piotr
ob.name = "Grzegorz";
console.log(ob.name); //Grzegorz

ob.height = 160;
console.log(ob.height); //160cm

ob.gender = "woman"; //dalej "male" - nie możemy zmienić tej właściwości

Powyższe definiowanie właściwości możemy też połączyć z konstruktorem:


function Car(brand, color) {
    this.brand = brand;
    this.color = color;

    let _speed; //zmienna prywatna niedostępna z zewnątrz

    Object.defineProperty(this, "speed", {
        get: function() {
            return this._speed;
        },
        set: function(value) {
            this._speed = value;
            if (this._speed > 180) {
                this._speed = 180;
            }
        }
    });
}

const car = new Car("BMW", "Black");
car.speed = 160;
car.speed += 20;
car.speed += 20; // 180 a nie 200!
console.log(car.speed); //180km

Enumerable

Jak spojrzysz powyżej, jedną z właściwości jest enumerable, która oznacza, czy dana zmienna ma być listowana gdy wykonujemy pętlę na danym obiekcie. Sprawdźmy to na przykładzie:


const tab = ["Ala", "Ola", "Ula"];
console.dir(tab); //0: "Ala", 1: "Ola", 2: "Ula", "length" : 3

Powyżej mamy tablicę - czyli obiekt. Jak widzisz, gdy wypisujemy nasz obiekt za pomocą console.dir, wypisane są jego wszystkie klucze wraz z length.

Jeżeli jednak zrobimy pętlę po takim obiekcie, zostaną uwzględnione tylko klucze iterowalne:


const tab = ["Ala", "Ola", "Ula"];
for (const key in tab) {
    console.log(key); //0, 1, 2, ale nie length
}

Spróbujmy to zrobić na naszym obiekcie:


const ob = {
    name : "Marcin",
    age : "old"
}
ob.pet = "Szamson";

for (const key in ob) {
    console.log(key); //name, age, pet
}

Oznacza to, że tworząc w klasyczny sposób obiekty, wszystkie ich klucze są iterowalne.

Korzystając z funkcji Object.create możemy zdecydować, które właściwości naszego obiektu będą iterowalne:


const ob = Object.create({}, {
    name : {
        enumerable : true,
        value : "Marcin"
    },
    age : {
        enumerable : false,
        value : 200,
        writable : false
    }
});

for (const i in ob) {
    console.log(i); //name
}

Żeby sprawdzić jakie cechy ma dana właściwość, możemy posłużyć się funkcją Object.getOwnPropertyDescriptor():


Object.getOwnPropertyDescriptor(ob, "name");
//{value: "Marcin", writable: false, enumerable: true, configurable: false}

Jeżeli temat cię zainteresował polecam dodatkowe artykuły na ten temat:

Trening czyni mistrza

Jeżeli chcesz sobie potrenować zdobytą wiedzę, zadania znajdują się w repozytorium pod adresem: https://github.com/kartofelek007/zadania

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem.
Aha - i ta strona korzysta z ciasteczek.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.