Rozszerzanie obiektu w JavaScript

164

Obecnie przechodzę z Java do Javascript i jest mi trochę trudno wymyślić, jak rozszerzać obiekty tak, jak chcę.

Widziałem kilka osób w Internecie, które używają metody zwanej rozszerzaniem obiektu. Kod będzie wyglądał następująco:

var Person = {
   name : 'Blank',
   age  : 22
}

var Robot = Person.extend({
   name : 'Robo',
   age  : 4
)}

var robot = new Robot();
alert(robot.name); //Should return 'Robo'

Czy ktoś wie, jak to działa? Słyszałem, że musisz pisać

Object.prototype.extend = function(...);

Ale nie wiem, jak sprawić, by ten system działał. Jeśli nie jest to możliwe, pokaż mi inną alternatywę, która rozszerza obiekt.

Wituz
źródło
powrót prawda; ale dlatego proszę :)
Wituz
2
proponuję przejść przez ten piękny samouczek na MDN: - developer.mozilla.org/en/ ...
Pranav
Jeśli po przeczytaniu tych ładnych dokumentów nadal ciekawi Cię extendfunkcja, ustawiłem tutaj przykład: jsfiddle.net/k9LRd
Codrin Eugeniu
2
Sugerowałbym również, aby nie myśleć o tym wyłącznie jako
Toni Leigh

Odpowiedzi:

195

Chcesz „dziedziczyć” po prototypowym obiekcie osoby:

var Person = function (name) {
    this.name = name;
    this.type = 'human';
};

Person.prototype.info = function () {
    console.log("Name:", this.name, "Type:", this.type);
};

var Robot = function (name) {
    Person.apply(this, arguments);
    this.type = 'robot';
};

Robot.prototype = Person.prototype;  // Set prototype to Person's
Robot.prototype.constructor = Robot; // Set constructor back to Robot

person = new Person("Bob");
robot = new Robot("Boutros");

person.info();
// Name: Bob Type: human

robot.info();
// Name: Boutros Type: robot
osahyoun
źródło
4
Mam jedno pytanie: jak Person()wywoływany jest konstruktor, kiedy to robisz new Robot()? Wydaje mi się, że powinieneś wywołać ten konstruktor klasy bazowej zamiast robić this.name = name;w Robot()konstruktorze ...
Alexis Wilke
21
@AlexisWilke: Tak, powinieneś zadzwonić Person.apply(this, arguments);. Byłoby też lepiej użyć Robot.prototype = Object.create(Person.prototype);zamiast new Person();.
Felix Kling
18
Jak stwierdził Felix, „Robot.prototype = Person.prototype;” to zły pomysł, jeśli ktoś chce, aby typ „Robot” miał własną prototypową instancję. Dodanie nowych funkcji specyficznych dla robota również dodałoby go do osoby.
James Wilkins
20
Ten przykład jest całkowicie błędny. W ten sposób zmieniasz prototyp Person. To nie jest dziedziczenie i możesz narazić się na ogromny bałagan w klasie Person. Zobacz odpowiedź, która zaleca użycie Object.create (). To jest właściwy sposób robienia rzeczy.
nicolas-van
6
@osahyoun ta odpowiedź ma wysoką pozycję w wyszukiwarce Google. Naprawdę zasugerowałbym naprawienie kodu i poprawienie łańcucha prototypów, jak sugerują inne komentarze tutaj.
raphaëλ
101

Świat bez słowa kluczowego „nowe”.

I prostsza "prozaiczna" składnia z Object.create ().

* Ten przykład został zaktualizowany dla klas ES6.

Po pierwsze, pamiętaj, że JavaScript jest językiem prototypowym . Nie jest oparta na klasach. W związku z tym pisanie w formie prototycznej ujawnia jego prawdziwą naturę i może być bardzo proste, przypominające prozę i potężne.

TLDR;

const Person = { name: 'Anonymous' } // person has a name

const jack = Object.create(Person)   // jack is a person
jack.name = 'Jack'                   // and has a name 'Jack'

Nie, nie potrzebujesz konstruktorów, żadnych newinstancji ( przeczytaj, dlaczego nie powinieneś ich używaćnew ), nie super, żadnych zabawnych zabawnych __construct. Po prostu tworzysz obiekty, a następnie rozszerzasz je lub przekształcasz.

( Jeśli znasz metody pobierające i ustawiające, zobacz sekcję „Dalsze czytanie”, aby zobaczyć, w jaki sposób ten wzorzec zapewnia bezpłatne metody pobierające i ustawiające w sposób, do którego pierwotnie był przeznaczony JavaScript , i jakie one są potężne ).

Składnia podobna do prozy: Prototyp podstawowy

const Person = {

   //attributes
   firstName : 'Anonymous', 
   lastName: 'Anonymous',
   birthYear  : 0,
   type : 'human',

   //methods
   name() { return this.firstName + ' ' + this.lastName },
   greet() {
       console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' )
   },
   age() {
      // age is a function of birth time.
   }
}

const person = Object.create(Person). // that's it!

Na pierwszy rzut oka wygląda bardzo czytelnie.

Rozszerzenie, tworząc potomka Person

* Prawidłowe terminy to prototypesi ich descendants. Nie ma classesi nie ma takiej potrzeby instances.

const Skywalker = Object.create(Person)
Skywalker.lastName = 'Skywalker'

const anakin = Object.create(Skywalker)
anakin.firstName = 'Anakin'
anakin.birthYear = '442 BBY'
anakin.gender = 'male' // you can attach new properties.
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'

Person.isPrototypeOf(Skywalker) // outputs true
Person.isPrototypeOf(anakin) // outputs true
Skywalker.isPrototypeOf(anakin) // outputs true

Jednym ze sposobów zapewnienia „domyślnego” sposobu tworzenia descendantjest dołączenie #createmetody:

Skywalker.create = function(firstName, gender, birthYear) {

    let skywalker = Object.create(Skywalker)

    Object.assign(skywalker, {
        firstName,
        birthYear,
        gender,
        lastName: 'Skywalker',
        type: 'human'
    })

    return skywalker
}

const anakin = Skywalker.create('Anakin', 'male', '442 BBY')

Poniższe sposoby mają niższą czytelność:

Porównaj z „klasycznym” odpowiednikiem:

function Person (firstName, lastName, birthYear, type) {
    this.firstName = firstName 
    this.lastName = lastName
    this.birthYear = birthYear
    this.type = type
}

// attaching methods
Person.prototype.name = function() { return firstName + ' ' + lastName }
Person.prototype.greet = function() { ... }
Person.prototype.age = function() { ... }

function Skywalker(firstName, birthYear) {
    Person.apply(this, [firstName, 'Skywalker', birthYear, 'human'])
}

// confusing re-pointing...
Skywalker.prototype = Person.prototype
Skywalker.prototype.constructor = Skywalker

const anakin = new Skywalker('Anakin', '442 BBY')

Person.isPrototypeOf(anakin) // returns false!
Skywalker.isPrototypeOf(anakin) // returns false!

Czytelność kodu przy użyciu stylu „klasycznego” nie jest dobra.

Klasy ES6

Wprawdzie niektóre z tych problemów są eliminowane przez klasy ES6, ale nadal:

class Person {
    constructor(firstName, lastName, birthYear, type) {
        this.firstName = firstName 
        this.lastName = lastName
        this.birthYear = birthYear
        this.type = type
    }
    name() { return this.firstName + ' ' + this.lastName }
    greet() { console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' ) }
}

class Skywalker extends Person {
    constructor(firstName, birthYear) {
        super(firstName, 'Skywalker', birthYear, 'human')
    }
}

const anakin = new Skywalker('Anakin', '442 BBY')

// prototype chain inheritance checking is partially fixed.
Person.isPrototypeOf(anakin) // returns false!
Skywalker.isPrototypeOf(anakin) // returns true

Rozgałęzienie podstawowego prototypu

// create a `Robot` prototype by extending the `Person` prototype:
const Robot = Object.create(Person)
Robot.type = 'robot'
Robot.variant = '' // add properties for Robot prototype

Dołącz metody unikalne dla Robot

// Robots speak in binaries, so we need a different greet function:
Robot.machineGreet = function() { /*some function to convert strings to binary */ }

// morphing the `Robot` object doesn't affect `Person` prototypes
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
anakin.machineGreet() // error

Sprawdzam dziedziczenie

Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false

Masz już wszystko, czego potrzebujesz! Bez konstruktorów, bez instancji. Czysta, klarowna proza.

Dalsza lektura

Zapisywalność, konfigurowalność oraz bezpłatne metody pobierające i ustawiające!

Aby uzyskać bezpłatne metody pobierające i ustawiające lub dodatkową konfigurację, możesz użyć drugiego argumentu Object.create (), znanego również jako propertiesObject. Jest również dostępny w # Object.defineProperty i # Object.defineProperties .

Aby zilustrować, jak potężne jest to, załóżmy, że chcemy, aby wszystko Robotbyło ściśle wykonane z metalu (przez writable: false) i ujednolicić powerConsumptionwartości (za pomocą pobierających i ustawiających).

const Robot = Object.create(Person, {
    // define your property attributes
    madeOf: { 
        value: "metal",
        writable: false,
        configurable: false,
        enumerable: true
    },
    // getters and setters, how javascript had (naturally) intended.
    powerConsumption: {
        get() { return this._powerConsumption },
        set(value) { 
            if (value.indexOf('MWh')) return this._powerConsumption = value.replace('M', ',000k') 
            this._powerConsumption = value
            throw new Error('Power consumption format not recognised.')
        }  
    }
})

const newRobot = Object.create(Robot)
newRobot.powerConsumption = '5MWh'
console.log(newRobot.powerConsumption) // outputs 5,000kWh

A wszystkie prototypy Robotnie mogą być madeOfczymś innym, ponieważ writable: false.

const polymerRobot = Object.create(Robot)

polymerRobot.madeOf = 'polymer'

console.log(polymerRobot.madeOf) // outputs 'metal'

Miksery (za pomocą # Object. assign) - Anakin Skywalker

Czy wyczuwasz, dokąd to zmierza ...?

const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped because you get the point by now.

Object.assign(darthVader, Robot)

Darth Vader otrzymuje metody Robot:

darthVader.greet() // inherited from `Person`, outputs "Hi, my name is Darth Vader..."
darthVader.machineGreet() // inherited from `Robot`, outputs 001010011010...

Wraz z innymi dziwnymi rzeczami:

console.log(darthVader.type) // outputs robot.
Robot.isPrototypeOf(darthVader) // returns false.
Person.isPrototypeOf(darthVader) // returns true.

Cóż, to, czy Darth Vader jest człowiekiem, czy maszyną, jest rzeczywiście subiektywne:

„Jest teraz bardziej maszyną niż człowiekiem, pokręconym i złym”. - Obi-Wan Kenobi

„Wiem, że jest w tobie dobro”. - Luke Skywalker

Extra - nieco krótsza składnia z # Object. assign

Najprawdopodobniej ten wzorzec skraca twoją składnię. Ale ES6 # Object. assign może jeszcze bardziej skrócić (aby użyć polyfill w starszych przeglądarkach, zobacz MDN w ES6 ).

//instead of this
const Robot = Object.create(Person)
Robot.name = "Robot"
Robot.madeOf = "metal"

//you can do this
const Robot = Object.create(Person)
Object.assign(Robot, {
    name: "Robot",
    madeOf: "metal"
    // for brevity, you can imagine a long list will save more code.
})
Calvintwr
źródło
7
mieć głos za nieużywaniem funkcji konstruktora.
nsmarks
1
„wyszkoleni klasycznie” programiści, co przez to rozumiesz?
Petra
1
Pochodzę z klasycznego nastawienia OOP i ta odpowiedź bardzo mi pomogła. Dwa pytania dotyczące kodu: 1) Czy dzisiejszy ES2015 Object.assign(Robot, {a:1}jest dobrą alternatywą dla twojej extend()metody? 2) Jak nadpisać greet()metodę, aby zwracała ten sam tekst, ale z dopiskiem „a greet override”?
Barry Staes
2
1) #Object.assignwygląda na dobrą alternatywę. Ale obsługa przeglądarek jest niższa. 2) Użyjesz __proto__właściwości obiektu, aby uzyskać dostęp do funkcji powitania jego prototypu. następnie wywołujesz prototypową funkcję greet z przekazanym zakresem wywoływanego. w tym przypadku funkcja była dziennikiem konsoli, więc nie można jej „dopisać”. Ale w tym przykładzie myślę, że masz dryf. skywalker.greet = function() { this.__proto__.greet.call(this); console.log('a greet override'); }
Calvintwr
1
Cóż, taka dyskusja powinna zostać przeprowadzona z opiekunami specyfikacji języka ECMAScript. Generalnie się zgadzam, ale muszę pracować z tym, co mam.
51

Jeśli jeszcze nie wymyśliłeś sposobu, użyj właściwości asocjacyjnej obiektów JavaScript, aby dodać funkcję rozszerzającą do funkcji, Object.prototypejak pokazano poniżej.

Object.prototype.extend = function(obj) {
   for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
         this[i] = obj[i];
      }
   }
};

Możesz następnie użyć tej funkcji, jak pokazano poniżej.

var o = { member: "some member" };
var x = { extension: "some extension" };

o.extend(x);
tomilay
źródło
18
Pamiętaj, że spowoduje to utworzenie wskaźników do oryginalnego obiektu w klasie „potomnej” podczas używania obiektów / tablic w klasie „nadrzędnej”. Aby rozwinąć: Jeśli masz obiekt lub tablicę w swojej klasie nadrzędnej, zmodyfikowanie go w klasie potomnej, która rozszerza się na tej podstawie, faktycznie zmodyfikuje go dla wszystkich klas potomnych, które rozszerzają się na tę samą klasę bazową.
Harold
Harold, dziękuję za podkreślenie tego faktu. Dla każdego, kto używa tej funkcji, ważne jest, aby zawierał warunek, który sprawdza obiekty / tablice i tworzy ich kopie.
tomilay
30

Inne podejście: Object.create

Zgodnie z odpowiedzią @osahyoun, uważam, że następujące sposoby są lepszym i wydajnym sposobem „dziedziczenia” z prototypowego obiektu Person:

function Person(name){
    this.name = name;
    this.type = 'human';
}

Person.prototype.info = function(){
    console.log("Name:", this.name, "Type:", this.type);
}

function Robot(name){
    Person.call(this, name)
    this.type = 'robot';
}

// Set Robot's prototype to Person's prototype by
// creating a new object that inherits from Person.prototype,
// and assigning it to Robot.prototype
Robot.prototype = Object.create(Person.prototype);

// Set constructor back to Robot
Robot.prototype.constructor = Robot;

Utwórz nowe instancje:

var person = new Person("Bob");
var robot = new Robot("Boutros");

person.info(); // Name: Bob Type: human
robot.info();  // Name: Boutros Type: robot

Teraz, używając Object.create :

Person.prototype.constructor !== Robot

Sprawdź również dokumentację MDN .

Lior Elrom
źródło
2
Chcę tylko powiedzieć, że @GaretClaborn działa poprawnie, ale nie przekazujesz nameparametru do konstruktora nadrzędnego, na przykład: jsfiddle.net/3brm0a7a/3 (różnica jest w linii # 8)
xPheRe
1
@xPheRe Ah Rozumiem, dzięki. Zredagowałem odpowiedź, aby odzwierciedlić tę zmianę
Garet Claborn
1
@xPheRe, wydaje mi się, że bardziej skupiłem się na udowodnieniu punktu, kiedy dodałem to rozwiązanie. Dzięki.
Lior Elrom
1
Dobra odpowiedź +1, możesz rzucić okiem na ECMAScript 6. Klasa i rozszerzenia słów kluczowych są dostępne: developer.mozilla.org/en-US/docs/Web/JavaScript/ ...
Benjamin Poignant
26

W ES6 możesz używać operatora rozszerzania, takiego jak

var mergedObj = { ...Obj1, ...Obj2 };

Zauważ, że Object. assign () wyzwala settery, podczas gdy składnia spreadu nie.

Aby uzyskać więcej informacji, zobacz link, Składnia MDN -Spread


Stara odpowiedź:

W ES6 istnieje Object.assignmożliwość kopiowania wartości właściwości. Użyj {}jako pierwszego parametru, jeśli nie chcesz modyfikować obiektu docelowego (pierwszy przekazany parametr).

var mergedObj = Object.assign({}, Obj1, Obj2);

Aby uzyskać więcej informacji, zobacz link, MDN - Object. assign ()

W przypadku, gdy potrzebujesz Polyfill dla ES5 , link również go oferuje. :)

KrIsHnA
źródło
18

A rok później mogę powiedzieć, że jest jeszcze jedna miła odpowiedź.

Jeśli nie podoba ci się sposób, w jaki działa prototypowanie w celu rozszerzenia na obiekty / klasy, spójrz na to: https://github.com/haroldiedema/joii

Szybki przykładowy kod możliwości (i wiele więcej):

var Person = Class({

    username: 'John',
    role: 'Employee',

    __construct: function(name, role) {
        this.username = name;
        this.role = role;
    },

    getNameAndRole: function() {
        return this.username + ' - ' + this.role;
    }

});

var Manager = Class({ extends: Person }, {

  __construct: function(name)
  {
      this.super('__construct', name, 'Manager');
  }

});

var m = new Manager('John');
console.log(m.getNameAndRole()); // Prints: "John - Manager"
Harold
źródło
Cóż, mam jeszcze 2 miesiące do końca 2 lat: P Tak czy inaczej, JOII 3.0 niedługo się wyda :)
Harold
1
Zrób to 3 lata później.
Ciekawa koncepcja, ale składnia wygląda naprawdę brzydko. Lepiej byłoby poczekać, aż zajęcia ES6 się ustabilizują
senny
Całkowicie się zgadzam @sleepycal. Ale niestety minie jeszcze co najmniej 5 lat, zanim zaimplementują to wszystkie główne / popularne przeglądarki. Więc do tego czasu to będzie musiało wystarczyć ...
Harold
12

Osoby, które wciąż walczą o proste i najlepsze podejście, możesz wykorzystać Spread Syntaxdo rozbudowy obiektu.

var person1 = {
      name: "Blank",
      age: 22
    };

var person2 = {
      name: "Robo",
      age: 4,
      height: '6 feet'
    };
// spread syntax
let newObj = { ...person1, ...person2 };
console.log(newObj.height);

Uwaga: Pamiętaj, że pierwszeństwo ma nieruchomość położona najdalej na prawo. W tym przykładzie person2jest po prawej stronie, więc newObjbędzie zawierał nazwę Robo .

Ali Shahbaz
źródło
6

Mozilla 'ogłasza' obiekt rozszerzający ECMAScript 6.0:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends

UWAGA: Jest to technologia eksperymentalna, część propozycji ECMAScript 6 (Harmony).

class Square extends Polygon {
  constructor(length) {
    // Here, it calls the parent class' constructor with lengths
    // provided for the Polygon's width and height
    super(length, length);
    // Note: In derived classes, super() must be called before you
    // can use 'this'. Leaving this out will cause a reference error.
    this.name = 'Square';
  }

  get area() {
    return this.height * this.width;
  }

  set area(value) {
    this.area = value;     } 
}

Ta technologia jest dostępna w Gecko (Google Chrome / Firefox) - wersje nocne 03/2015.

Niek Vandael
źródło
4

W większości projekcie istnieją pewne realizacja obiektu rozszerzenie: podkreślenia, jQuery, lodash: przedłużyć .

Istnieje również czysta realizacja JavaScript, który jest częścią ECMAScript 6: Object.assign : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Cezary Daniel Nowak
źródło
Czy „czysta implementacja javascript” nie odnosi się do czegoś, co jest zaimplementowane za pomocą samego JavaScript, a nie do funkcji dostarczonej przez środowisko, która może być implementowana natywnie?
binki
1
@binki, chodziło mi o natywną implementację javascript - część standardu ECMAScript 2015 (ES6)
Cezary Daniel Nowak
2
Function.prototype.extends=function(ParentClass) {
    this.prototype = new ParentClass();
    this.prototype.constructor = this;
}

Następnie:

function Person() {
    this.name = "anonym"
    this.skills = ["abc"];
}
Person.prototype.profile = function() {
    return this.skills.length // 1
};

function Student() {} //well extends fom Person Class
Student.extends(Person)

var s1 = new Student();
s1.skills.push("")
s1.profile() // 2

Aktualizacja 01/2017:

Proszę, zignoruj ​​moją odpowiedź z 2015 roku, ponieważ JavaScript obsługuje teraz extendssłowo kluczowe od ES6 (Ecmasctipt6)

- ES6:

class Person {
   constructor() {
     this.name = "anonym"
     this.skills = ["abc"];
   }

   profile() {
    return this.skills.length // 1
   }

}

Person.MAX_SKILLS = 10;
class Student extends Person {


} //well extends from Person Class

//-----------------
var s1 = new Student();
s1.skills.push("")
s1.profile() // 2

- ES7:

class Person {
    static MAX_SKILLS = 10;
    name = "anonym"
    skills = ["abc"];

    profile() {
      return this.skills.length // 1
    }

}
class Student extends Person {


} //well extends from Person Class

//-----------------
var s1 = new Student();
s1.skills.push("")
s1.profile() // 2
Abdennour TOUMI
źródło
1
Wywołując new ParentClass()przed nadpisaniem konstruktora, wykonałeś już konstruktora nadrzędnego. Nie sądzę, żeby to było właściwe zachowanie, jeśli mnie zapytasz ...
Harold
1

Podsumowanie:

Javascript używa mechanizmu, który nazywa się dziedziczeniem prototypowym . Dziedziczenie prototypowe jest używane podczas wyszukiwania właściwości obiektu. Kiedy rozszerzamy właściwości w javascript, dziedziczymy te właściwości z rzeczywistego obiektu. Działa w następujący sposób:

  1. Kiedy żądana jest właściwość obiektu (np. myObj.fooLub myObj['foo']), silnik JS najpierw szuka tej właściwości w samym obiekcie
  2. Gdy ta właściwość nie zostanie znaleziona w samym obiekcie, będzie wspinać się po łańcuchu prototypów i patrzeć na obiekt prototypowy. Jeśli ta właściwość również nie zostanie tutaj znaleziona, będzie wspinać się po łańcuchu prototypu, aż właściwość zostanie znaleziona. Jeśli właściwość nie zostanie znaleziona, zgłosi błąd odwołania.

Kiedy chcemy rozszerzyć obiekt w javascript, możemy po prostu połączyć ten obiekt w łańcuch prototypów. Jest wiele sposobów, aby to osiągnąć, opiszę 2 powszechnie stosowane metody.

Przykłady:

1. Object.create()

Object.create()jest funkcją, która przyjmuje obiekt jako argument i tworzy nowy obiekt. Obiekt przekazany jako argument będzie prototypem nowo utworzonego obiektu. Na przykład:

// prototype of the dog
const dogPrototype = {
  woof: function () { console.log('woof'); }
}

// create 2 dog objects, pass prototype as an argument
const fluffy = Object.create(dogPrototype);
const notFluffy = Object.create(dogPrototype);

// both newly created object inherit the woof 
// function from the dogPrototype
fluffy.woof();
notFluffy.woof();

2. Jawne ustawienie właściwości prototypu

Podczas tworzenia obiektów za pomocą funkcji konstruktora możemy ustawić właściwości add do właściwości obiektu prototypowego. Obiekty, które są tworzone z funkcji konstruktora przy użyciu newsłowa kluczowego, mają swój prototyp ustawiony na prototyp funkcji konstruktora. Na przykład:

// Constructor function object
function Dog (name) {
   name = this.name;
}

// Functions are just objects
// All functions have a prototype property
// When a function is used as a constructor (with the new keyword)
// The newly created object will have the consturctor function's
// prototype as its prototype property
Dog.prototype.woof = function () {
  console.log('woof');
}

// create a new dog instance
const fluffy = new Dog('fluffyGoodBoyyyyy');
// fluffy inherits the woof method
fluffy.woof();

// can check the prototype in the following manner
console.log(Object.getPrototypeOf(fluffy));

Willem van der Veen
źródło
0

Możesz to po prostu zrobić za pomocą:

Object.prototype.extend = function(object) {
  // loop through object 
  for (var i in object) {
    // check if the extended object has that property
    if (object.hasOwnProperty(i)) {
      // mow check if the child is also and object so we go through it recursively
      if (typeof this[i] == "object" && this.hasOwnProperty(i) && this[i] != null) {
        this[i].extend(object[i]);
      } else {
        this[i] = object[i];
      }
    }
  }
  return this;
};

aktualizacja: sprawdziłem, this[i] != nullponieważ nulljest to obiekt

Następnie użyj go tak:

var options = {
      foo: 'bar',
      baz: 'dar'
    }

    var defaults = {
      foo: false,
      baz: 'car',
      nat: 0
    }

defaults.extend(options);

To dobrze skutkuje:

// defaults will now be
{
  foo: 'bar',
  baz: 'dar',
  nat: 0
}
Mustafa Dwekat
źródło
0

PROSZĘ DODAĆ POWÓD DO DOWNVOTE

  • Nie ma potrzeby korzystania z żadnej zewnętrznej biblioteki do rozszerzenia

  • W JavaScript wszystko jest obiektem (poza trzema prymitywnymi typami danych, a nawet one są automatycznie zawijane obiektami w razie potrzeby). Ponadto wszystkie obiekty są zmienne.

Klasa Person w JavaScript

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype = {
    getName: function() {
        return this.name;
    },
    getAge: function() {
        return this.age;
    }
}

/* Instantiate the class. */
var alice = new Person('Alice', 93);
var bill = new Person('Bill', 30);

Zmodyfikuj konkretną instancję / obiekt .

alice.displayGreeting = function() 
{
    alert(this.getGreeting());
}

Zmodyfikuj klasę

Person.prototype.getGreeting = function() 
{
    return 'Hi ' + this.getName() + '!';
};

Lub po prostu powiedz: rozszerz JSON i OBJECT oba są takie same

var k = {
    name : 'jack',
    age : 30
}

k.gender = 'male'; /*object or json k got extended with new property gender*/

dzięki ross harmes, dustin diaz

vijay
źródło
-1

Spowoduje to, że rozszerzone właściwości utworzą nowy obiekt z prototypami parametrów obiektu bez zmiany przekazanego obiektu.

function extend(object) {
    if (object === null)
        throw TypeError;
    if (typeof object !== "object" && typeof object !== "function")
        throw TypeError;
    if (Object.create)
        return Object.create(object);
    function f() {}
    ;
    f.prototype = p;
    return new f();
}

Ale jeśli chcesz rozszerzyć obiekt bez modyfikowania jego parametrów, możesz dodać do niego extensionProperty.

var Person{
//some code
extend: extendProperty
}

//Enforce type checking an Error report as you wish
    function extendProperty(object) {
        if ((object !== null && (typeof object === "object" || typeof object === "function"))){
            for (var prop in object) {
                if (object.hasOwnProperty(prop))
                    this[prop] = object[prop];
            }
        }else{
            throw TypeError; //Not an object
        }
    }
Ndifreke
źródło
-2

Prototypowanie to fajny sposób, ale prototyp jest czasami dość niebezpieczny i może prowadzić do błędów. Wolę zamknąć to w obiekcie podstawowym, tak jak robi to Ember.js z Ember.Object.extend i Ember.Object.reopen. To jest znacznie bezpieczniejsze w użyciu.

Stworzyłem streszczenie, w jaki sposób można skonfigurować coś podobnego do tego, czego używa Ember.Object.

Oto link: https://gist.github.com/WebCloud/cbfe2d848c80d4b9e9bd

Vinicius Dallacqua
źródło
9
Prototyping is a nice way, but prototype is quite dangerous sometimes and can lead to bugs.Co przez to rozumiesz? Używanie łańcucha prototypów w JavaScript może prowadzić do błędów? To tak, jakby powiedzieć, że używanie klas w Javie może prowadzić do błędów i nie ma absolutnie żadnego sensu.
HMR
@HMR mówi, że rozszerzenie prototypów obiektów dostarczanych przez środowisko skutkuje delikatnym kodem, który może kolidować z przyszłą podstawową funkcją języka JavaScript. Jeśli dodasz użyteczną funkcję narzędzia do wszystkiego, rozszerzając Objectprototyp, twoja funkcja może mieć taką samą nazwę jak przyszła funkcja JavaScript i spowodować eksplozję kodu, gdy zostanie uruchomiona w przyszłości. Na przykład załóżmy, że dodałeś repeat()funkcję do Objecti wywołałeś ją na Stringinstancjach, a następnie twoje środowisko wykonawcze JavaScript zostało zaktualizowane do ES6?
binki
@binki Dziękuję za wkład. Mówisz o zmianie prototypu klas, których nie "posiadasz", a tym samym zerwaniu odniesienia do inkapsulacji: developer.mozilla.org/en/docs/Web/JavaScript/ ... JS nie ma prywatnych zmiennych, więc twoje API ujawnia członków implementacji , zwykle rozwiązuje się to według konwencji (nazwę członka należy zaczynać od podkreślenia). Nie jestem pewien, czy to jest główny problem, który ma op, czy też składnia jest zagmatwana i wiele osób tego nie rozumie.
HMR
@HMR, mogę się mylić, ale myślę, że „ale prototyp jest dość niebezpieczny” odnosi się do niesławnego szkieletu prototypów, który może nadużywać funkcji prototypejęzyka .
binki
Prototypowanie jest niebezpieczne, ponieważ jeśli używasz obiektów, których nie stworzyłeś, nie zawsze wiesz, jakie będą skutki uboczne używania ich jako prototypów. Spójrz na to skrzypce, na przykład: jsfiddle.net/fo6r20rg
Arkain