Расширение объекта в Javascript

В настоящее время я перехожу с Java на Javascript, и мне немного сложно понять, как расширять объекты так, как я хочу.

Я видел, как несколько человек в Интернете использовали метод под названием extend on object. Код будет выглядеть так:

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

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

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

Кто-нибудь знает, как заставить это работать? Я слышал что тебе нужно написать

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

Но я не знаю, как заставить эту систему работать. Если это невозможно, покажите мне другой вариант расширения объекта.


person Wituz    schedule 03.05.2012    source источник
comment
вернуть истину; но именно поэтому я спрашиваю :)   -  person Wituz    schedule 03.05.2012
comment
Я бы посоветовал пройти через этот красивый учебник по MDN: - developer.mozilla.org/en/ < / а>   -  person Pranav    schedule 03.05.2012
comment
Я бы также посоветовал не думать об этом строго как о «преобразовании с Java на JavaScript» и более как об «изучении нового языка, Javascript, который имеет синтаксис, аналогичный Java».   -  person Toni Leigh    schedule 18.02.2015


Ответы (16)


Вы хотите «унаследовать» от объекта-прототипа Person:

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
person osahyoun    schedule 03.05.2012
comment
У меня один вопрос: как вызывается конструктор Person(), когда вы делаете new Robot()? Мне кажется, что вы должны вызвать этот конструктор базового класса вместо того, чтобы делать this.name = name; в конструкторе Robot() ... - person Alexis Wilke; 08.04.2014
comment
@AlexisWilke: Ага, тебе следует позвонить Person.apply(this, arguments);. Также было бы лучше использовать Robot.prototype = Object.create(Person.prototype); вместо new Person();. - person Felix Kling; 16.04.2014
comment
Как заявил Феликс, «Robot.prototype = Person.prototype;» - плохая идея, если кто-то хочет, чтобы тип «Робот» имел собственный экземпляр прототипа. Добавление новых функций, специфичных для роботов, также добавит их человеку. - person James Wilkins; 27.05.2014
comment
Вместо присвоения Person.prototype; в Robot.prototype используйте: Object.create (Person.prototype) - person Lior Elrom; 14.08.2014
comment
что именно это Person.apply(this,arguments) делает? - person ProblemsOfSumit; 13.11.2014
comment
@Sumit Он применяет функцию конструктора Person к функции конструктора робота. Это автоматический способ запуска тела одной функции в контексте тела другой функции. Вы можете заменить Person.apply(this,arguments) на this.name = name; this.type = 'human';, и это будет иметь тот же эффект ... до тех пор, пока вам не понадобится изменить Person, после чего робот не будет автоматически изменен для соответствия. Не забудьте обновить робота, чтобы он соответствовал вашим требованиям. - person DaveS; 12.12.2014
comment
Этот пример совершенно неверен. Тем самым вы изменяете прототип Person. Это не наследование, и вы рискуете внести огромный беспорядок в класс Person. См. Ответ, в котором рекомендуется использовать Object.create (). Это правильный способ делать что-то. - person nicolas-van; 12.12.2014
comment
@ user2491400 Это не совсем так. Он выполнит основную работу по расширению объекта Person. Но что более важно, это то, что работает для IE ‹= 8. Если у вас нет пользователей IE 6/7/8, использующих ваш javascript, определенно используйте новый улучшенный метод Object.create. - person DaveS; 12.12.2014
comment
@DaveS Это неверно, поскольку он не расширяет класс Person. Расширение класса предполагает как минимум создание нового прототипа. Здесь, если мы хотим добавить методы в Robot, мы не можем этого сделать, не изменив также прототип Person. Единственная разница между Человеком и Роботом - это конструктор. И отсутствие Object.create () не является оправданием, поскольку методы расширения класса существовали задолго до того, как появился Object.create (). Простая форма - сделать Robot.prototype = new Person();. Это позор, и его следует удалить, поскольку он может ввести в заблуждение младших разработчиков. - person nicolas-van; 14.12.2014
comment
@ user2491400 Вы правы, но вместо того, чтобы удалить его, @osahyoun следует отредактировать его, чтобы использовать Robot.prototype = new Person(); вместо своих двух строк с прототипом. И, возможно, добавьте аннотацию о Object.create() в современных браузерах для получения бонусных баллов. - person DaveS; 15.12.2014
comment
@osahyoun этот ответ имеет высокий рейтинг в поиске Google. Я действительно предлагаю вам исправить код и исправить цепочку прототипов, как это предлагают другие комментарии здесь. - person raphaëλ; 22.01.2015
comment
Возможно, это выглядит не так красиво, как Robot.prototype = new Person (обратите внимание на отсутствие круглых скобок; в этом случае вам также следует создать экземпляр без вызова конструктора, если вы хотите избежать выдачи ошибок из-за недопустимых аргументов конструктора), но обычно я бы сделал что-то вроде Robot.prototype.__proto__ = Person.prototype. Это не только приводит к тому же Robot.prototype, но также экономит немного памяти, поскольку не создает лишний неиспользуемый экземпляр Person. - person Patrick Roberts; 29.06.2015
comment
Если я опущу Robot.prototype.constructor = Robot;, я все равно получу объект robot с правильным выводом. Какая здесь польза от конструктора - он служит только для идентификации класса, но не меняет поведение при создании? - person andig; 07.08.2016
comment
Меня обманул этот ответ, и действительно, первый расширенный объект перезаписывается новым. Прочтите ответ от @Lior Elrom ниже, чтобы предотвратить нежелательные побочные эффекты: Другой подход: Object.create - person Gregzenegair; 24.07.2018
comment
Это не называется расширением, это просто копирование прототипа. Любые изменения в каждом прототипе повлияют на все прототипы, поэтому переопределение функции невозможно при изменении прототипа без обходного пути. - person Javid; 19.09.2018
comment
Комментарии пользователя 2491400 удалены, поэтому удалите это Правка: перед использованием кода проверьте комментарии пользователя 2491400, который сообщает о побочных эффектах простого присвоения прототипу. - person ; 25.05.2019

Мир без нового ключевого слова.

И более простой прозаический синтаксис с Object.create ().

* Этот пример обновлен для классов ES6 и TypeScript.

Во-первых, на самом деле Javascript - это язык-прототип, а не на основе классов. Его истинная природа выражена в приведенной ниже прототипной форме, которая, как вы, возможно, заметили, очень проста, похожа на прозу, но все же действенна.

TL; DR;

const Person = { 
    name: 'Anonymous', // person has a name
    greet: function() { console.log(`Hi, I am ${this.name}.`} 
} 
    
const jack = Object.create(Person)   // jack is a person
jack.name = 'Jack'                   // and has a name 'Jack'
jack.greet()                         // outputs "Hi, I am Jack."

Это устраняет иногда запутанный шаблон конструктора. Новый объект наследует старый, но может иметь свои собственные свойства. Если мы попытаемся получить член из нового объекта (#greet()), которого не хватает новому объекту jack, старый объект Person предоставит член.

Выражаясь словами Дугласа Крокфорда: объекты наследуются от объектов. Что может быть более объектно-ориентированным, чем это?

Вам не нужны ни конструкторы, ни new создание экземпляров ( прочтите, почему не следует использовать new), ни super, ни самодельного __construct. Вы просто создаете объекты, а затем расширяете или изменяете их.

Этот шаблон также предлагает неизменность (частичную или полную) и геттеры / сеттеры.

Машинопись

Эквивалент TypeScript выглядит так же:

interface Person { 
    name:  string,  
    greet: Function
}

const Person =  {
    name:  'Anonymous',  
    greet:  function(): void  { console.log(`Hi, I am ${this.name}.` }
}  
const jack: Person = Object.create(Person)
jack.name = 'Jack'
jack.greet()

Прозаический синтаксис: Person protoype

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!

Чисто и ясно. Его простота не влияет на возможности. Читать дальше.

Создание потомка / копии Person

Примечание. Правильными терминами являются prototypes и их descendants/copies. Нет ни classes, ни 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

Если вы чувствуете себя менее безопасно, выбрасывая конструкторы вместо прямых заданий, справедливый момент. Один из распространенных способов - прикрепить #create метод:

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')

Разветвление прототипа Person на Robot

Когда вы ответвляете Robot потомка от Person прототипа, вы не влияете на Skywalker и anakin:

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

Прикрепите методы, уникальные для Robot

Robot.machineGreet = function() { 
    /*some function to convert strings to binary */ 
}

// Mutating the `Robot` object doesn't affect `Person` prototype and its descendants
anakin.machineGreet() // error

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

В TypeScript вам также потребуется расширить Person интерфейс:

interface Robot extends Person {
    machineGreet: Function
}
const Robot: Robot = Object.create(Person)
Robot.machineGreet = function(): void { console.log(101010) }

И у вас могут быть миксины - потому что ... Дарт Вейдер - человек или робот?

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

Дарт Вейдер использует методы Robot:

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

Наряду с другими странностями:

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

Что элегантно отражает субъективизм реальной жизни:

Теперь он больше машина, чем человек, извращенный и злой. - Оби-Ван Кеноби

Я знаю, что в тебе есть хорошее. - Люк Скайуокер

Сравните с классическим эквивалентом до ES6:

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')

// #isPrototypeOf won't work
Person.isPrototypeOf(anakin) // returns false
Skywalker.isPrototypeOf(anakin) // returns false

Если вы хотите повысить удобочитаемость кода, вам нужно выбрать классы ES6, которые стали более популярными и совместимы с браузерами (или не имеют отношения к babel):

Классы ES6

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

По общему признанию, некоторые из этих проблем устраняются классами ES6. Когда я писал свой ответ пять лет назад, классы ES6 только зарождались и теперь созрели для использования.

Но под капотом классов ES6 скрывается истинная прототипная природа Javascript. Поэтому, естественно, я был разочарован его реализацией.

Тем не менее, нельзя сказать, что классы ES6 плохие. Он предоставляет множество новых функций и стандартизирован в удобочитаемой форме. Хотя на самом деле не следовало использовать операторы class и new, чтобы запутать всю проблему.

дальнейшее чтение

Возможность записи, настраиваемость и бесплатные методы получения и установки!

Для бесплатных методов получения и установки или дополнительной конфигурации вы можете использовать второй аргумент Object.create (), также известный как propertiesObject. Он также доступен в # Object.defineProperty и # Object.defineProperties.

Чтобы проиллюстрировать его полезность, предположим, что мы хотим, чтобы все Robot были строго сделаны из металла (через writable: false) и стандартизировали powerConsumption значения (через геттеры и сеттеры).

const Robot = Object.create(Person, {
    // define your property attributes
    madeOf: { 
        value: "metal",
        writable: false,
        configurable: false,
        enumerable: true
    },
    // getters and setters
    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

И все прототипы Robot не могут быть madeOf чем-то еще:

const polymerRobot = Object.create(Robot)
polymerRobot.madeOf = 'polymer'
console.log(polymerRobot.madeOf) // outputs 'metal'
person Calvintwr    schedule 02.02.2015
comment
есть голос за то, что вы не используете функцию-конструктор. - person nsmarks; 04.06.2015
comment
- Object.create не существует во многих (старых) браузерах, особенно в Internet Explorer 8 и ниже. - Object.create () по-прежнему вызывает конструктор переданной через него функции. - Для каждого объявления свойства вам придется снова и снова настраивать одни и те же параметры (как вы показали в примере кода). Нет никакой реальной пользы от использования Object.create вместо ключевого слова new. - person Harold; 16.06.2015
comment
@Harold настоящая выгода приходит, когда вы пытаетесь объяснить 10-летнему ребенку, что делает код. Использование этого метода Object.create () намного проще для понимания, чем создание конструкторов, перенаправление прототипов или настройка классов. - person Calvintwr; 23.06.2015
comment
Программисты с классическим образованием, что вы имеете в виду? - person Petra; 14.08.2015
comment
Есть классический способ делать что-то и прототипный способ делать что-то. См. stackoverflow.com/questions/19633762/ - person Calvintwr; 14.08.2015
comment
Возможно, потому что это ЗВЕЗДНЫЕ ВОЙНЫ, и вы назвали его D2R2. Никогда не облажайся ЗВЕЗДНЫЕ ВОЙНЫ. - person Jimbo; 23.11.2015
comment
Если я прав, Robot.type = robot затеняет исходный Person.type. Также r2d2.name = 'R2D2' не записывает в Person.name, но создает новую переменную для объекта r2d2 - person psx; 11.12.2015
comment
ты прав. При вызове свойства (скажем, Robot.type) Javascript сначала пытается определить, существует ли оно на объекте (Robot), в противном случае я буду проходить по цепочке прототипов настолько далеко, насколько это необходимо, пока не найдет определенное свойство (Person.type), или когда он достигает базового прототипа (самого первого, из которого сделаны копии - Person). - person Calvintwr; 13.12.2015
comment
Я исхожу из классического мышления ООП, и этот ответ мне очень помог. Два вопроса по коду: 1) Является ли сегодняшний ES2015 Object.assign(Robot, {a:1} хорошей альтернативой вашему extend() методу? 2) Как переопределить метод greet(), чтобы он возвращал тот же текст, но с добавленным переопределением приветствия? - person Barry Staes; 25.03.2016
comment
1) #Object.assign действительно выглядит хорошей альтернативой. Но поддержка браузеров ниже атм. 2) Вы будете использовать свойство __proto__ объекта для доступа к функции приветствия его прототипа. затем вы вызываете функцию приветствия прототипа с переданной областью действия вызываемого. В этом случае функция была консольным журналом, поэтому ее невозможно добавить. Но я думаю, что в этом примере вы уловили дрейф. skywalker.greet = function() { this.__proto__.greet.call(this); console.log('a greet override'); } - person Calvintwr; 01.04.2016
comment
Object.create нельзя использовать для создания нового экземпляра Map или Set, а JavaScript не является бесклассовым языком. - person ; 03.02.2017
comment
@TinyGiant, возможно, вы захотите привести пример того, почему разрешение Object.create на создание нового экземпляра Map или Set так важно. - person Calvintwr; 10.05.2017
comment
... потому что без нового вы не можете использовать карту или набор? Я думал, что это очевидно. - person ; 10.05.2017
comment
@TinyGiant Понятно. Теперь я понимаю, что вы имеете в виду. Но вы должны принять во внимание, что map был представлен в 2015 году, потому что классические программисты хотят сделать синтаксис Javascript знакомым. Ибо не из-за сообщества это могло быть Object.create(Map) вместо new Map(). Я согласен с тем, что он движется к гибриду путем включения полезных вещей наиболее знакомыми способами, но мы не должны терять его интуитивный стиль из-за монстра классов и создания экземпляров, для которых нет причин. - person Calvintwr; 11.05.2017
comment
Что ж, это обсуждение, которое следует провести со специалистами по сопровождению спецификации языка ECMAScript. Я в целом согласен, но мне приходится работать с тем, что у меня есть. - person ; 11.05.2017
comment
Можно ли расширить несколько прототипов в одном объекте? - person Angger; 06.09.2017
comment
@Angger Можете ли вы привести пример того, чего вы пытаетесь достичь? - person Calvintwr; 09.09.2017
comment
Я не понимаю строку let self = this в функции Robot.charge(). Я имею в виду .... где-то они вызывают this., свойства, где-то self. и некоторые свойства без каких-либо добавлений .... - person Enriqe; 26.02.2019
comment
Я признаю, что обычно я избавляюсь от путаницы в this области видимости в таких сценариях (вызов другой функции из функции и передача ей аргументов, которые являются свойствами вызывающей ее функции), что устраняет проблемы совершения типичной ошибки. В этом конкретном сценарии let self = this и передача self в Math.min излишни. - person Calvintwr; 27.02.2019

Если вы еще не нашли способ, используйте ассоциативное свойство объектов JavaScript, чтобы добавить функцию расширения к Object.prototype, как показано ниже.

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

Затем вы можете использовать эту функцию, как показано ниже.

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

o.extend(x);
person tomilay    schedule 04.11.2013
comment
Помните, что это создаст указатели на исходный объект в «дочернем» классе при использовании объектов / массивов в «родительском» классе. Для уточнения: если у вас есть объект или массив в родительском классе, изменение его в дочернем классе, который расширяется на этой основе, фактически изменит его для всех дочерних классов, которые расширяются на этот же базовый класс. - person Harold; 06.10.2014
comment
Гарольд, Спасибо, что обратил внимание на этот факт. Для того, кто использует функцию, важно включить условие, которое проверяет объекты / массивы и делает их копии. - person tomilay; 09.10.2014

В ES6 вы можете использовать оператор распространения, например

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

Обратите внимание, что Object.assign () запускает сеттеры, а синтаксис распространения - нет.

Для получения дополнительной информации см. Ссылку MDN -Spread Syntax < / а>


Старый ответ:

В ES6 есть Object.assign для копирования значений свойств. Используйте {} в качестве первого параметра, если вы не хотите изменять целевой объект (первый переданный параметр).

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

Для получения дополнительных сведений см. Ссылку MDN - Object .assign ()

Если вам нужен Polyfill для ES5, ссылка также предлагает его. :)

person Midhun KM    schedule 15.01.2016

Другой подход: Object.create

Согласно ответу @osahyoun, я считаю, что следующее является лучшим и эффективным способом «наследования» от объекта-прототипа 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;

Создайте новые экземпляры:

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

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

Теперь с помощью Object.create :

Person.prototype.constructor !== Robot

Также проверьте документацию MDN.

person Lior Elrom    schedule 04.08.2014
comment
Просто хочу сказать @GaretClaborn, что он работает правильно, но вы не передаете параметр name родительскому конструктору, например: jsfiddle.net/3brm0a7a/3 (разница в строке №8) - person xPheRe; 23.03.2016
comment
@xPheRe Ах, понятно, спасибо. Я отредактировал ответ, чтобы отразить это изменение - person Garet Claborn; 24.03.2016
comment
@xPheRe, полагаю, я больше сосредоточился на доказательстве своей точки зрения, когда добавил это решение. Спасибо. - person Lior Elrom; 24.03.2016
comment
Хороший ответ +1, вы можете взглянуть на ECMAScript 6. Доступны класс ключевых слов и расширения: developer.mozilla.org/en-US/docs/Web/JavaScript/ - person Benjamin Poignant; 17.06.2016

И еще через год я могу вам сказать, что есть еще один хороший ответ.

Если вам не нравится, как работает прототипирование с целью расширения на объекты / классы, взгляните на это: https://github.com/haroldiedema/joii

Быстрый пример кода возможностей (и многое другое):

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"
person Harold    schedule 22.01.2014
comment
Что ж, у меня еще есть 2 месяца до истечения 2 лет: P В любом случае, JOII 3.0 вот-вот выйдет :) - person Harold; 07.11.2014
comment
Сделайте это через 3 года. - person ; 02.02.2015
comment
Интересная концепция, но синтаксис выглядит ужасно. Лучше подождать, пока классы ES6 станут стабильными - person sleepycal; 06.04.2015
comment
Полностью согласен @sleepycal. Но, к сожалению, пройдет еще как минимум 5 лет, прежде чем все основные / распространенные браузеры внедриют это. Так что до этого времени это нужно сделать ... - person Harold; 15.04.2015

Люди, которые все еще ищут простой и лучший подход, могут использовать Spread Syntax для расширения объекта.

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);

Примечание. Помните, что крайнее правое свойство имеет приоритет. В этом примере person2 находится справа, поэтому newObj будет иметь в нем имя Робо.

person Ali Shahbaz    schedule 08.10.2018

Возможно, вы захотите использовать вспомогательную библиотеку, например underscore.js, в которой есть это собственная реализация extend().

И это также хороший способ изучить исходный код. Очень полезна аннотированная страница исходного кода.

person 250R    schedule 03.05.2012
comment
Пример того, как работает _.extend() в underscore.js, довольно ясно показывает его функциональность: lostechies.com/chrismissal/2012/10/05/ - person Lemmings19; 03.10.2013

Mozilla объявляет об объекте, расширяющем ECMAScript 6.0:

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

ПРИМЕЧАНИЕ. Это экспериментальная технология, часть предложения 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;     } 
}

Эта технология доступна в Gecko (Google Chrome / Firefox) - ночные сборки 03/2015.

person Niek Vandael    schedule 09.04.2015

В большинстве проектов есть несколько реализаций расширения объекта: подчеркивание, jquery, lodash: extend.

Также существует реализация на чистом javascript, которая является частью ECMAscript 6: Object.assign: https://developer.mozilla.org/en-US/docs/Web/JavaScript./Reference/Global_Objects/Object/assign

person Cezary Daniel Nowak    schedule 14.03.2015
comment
Разве «чистая реализация javascript» не относится к чему-то, что реализовано только с помощью JavaScript, а не к функции, предоставляемой средой, которая может быть реализована изначально? - person binki; 11.11.2015
comment
@binki, я имел в виду встроенную реализацию javascript - часть стандарта ECMAScript 2015 (ES6) - person Cezary Daniel Nowak; 15.11.2015

Function.prototype.extends=function(ParentClass) {
    this.prototype = new ParentClass();
    this.prototype.constructor = this;
}

Потом:

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

Обновление 01/2017:

Пожалуйста, не обращайте внимания на мой ответ 2015 года, поскольку Javascript теперь поддерживает ключевое слово extends с 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
person Abdennour TOUMI    schedule 19.02.2015
comment
Вызывая new ParentClass() перед перезаписью конструктора, вы уже выполнили родительский конструктор. Я не думаю, что это правильное поведение, если вы спросите меня ... - person Harold; 16.06.2015

Резюме:

Javascript использует механизм, который называется прототипное наследование. Прототипное наследование используется при поиске свойства объекта. Когда мы расширяем свойства в javascript, мы наследуем эти свойства от реального объекта. Это работает следующим образом:

  1. Когда запрашивается свойство объекта (например, myObj.foo или myObj['foo']), JS-движок сначала ищет это свойство на самом объекте.
  2. Если это свойство отсутствует в самом объекте, оно поднимется по цепочке прототипов и будет смотреть на объект-прототип. Если это свойство также не найдено здесь, он будет продолжать подниматься по цепочке прототипов, пока свойство не будет найдено. Если свойство не найдено, будет выдана справочная ошибка.

Когда мы хотим расширить объект в javascript, мы можем просто связать этот объект в цепочке прототипов. Есть множество способов добиться этого, я опишу 2 наиболее часто используемых.

Примеры:

1. Object.create()

Object.create() - это функция, которая принимает объект в качестве аргумента и создает новый объект. Объект, переданный в качестве аргумента, будет прототипом вновь созданного объекта. Например:

// 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. Явная установка свойства прототипа

При создании объектов с использованием функций конструктора мы можем установить добавляемые свойства для его свойства объекта-прототипа. Объекты, которые создаются из функции конструктора при использовании ключевого слова new, имеют свой прототип, установленный на прототип функции конструктора. Например:

// 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));

person Willem van der Veen    schedule 03.10.2018

Вы можете просто сделать это, используя:

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;
};

обновление: я проверил this[i] != null, поскольку null является объектом

Затем используйте это как:

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

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

defaults.extend(options);

В результате получается:

// defaults will now be
{
  foo: 'bar',
  baz: 'dar',
  nat: 0
}
person Mustafa Dwekat    schedule 23.02.2016

ПОЖАЛУЙСТА, ДОБАВЬТЕ ПРИЧИНУ ДЛЯ ПРОТИВОГОЛОСОВАНИЯ

  • Нет необходимости использовать какую-либо внешнюю библиотеку для расширения

  • В JavaScript все является объектом (за исключением трех примитивных типов данных, и даже они автоматически обертываются объектами, когда это необходимо). Более того, все объекты изменяемы.

Class Person в 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);

Измените конкретный экземпляр / объект.

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

Измените класс

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

Или просто скажите: расширение JSON и OBJECT - одно и то же

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

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

спасибо Росс Хармес, Дастин Диаз

person vijay    schedule 20.04.2016

Хотя эта работа не на 100% правильна

// Parent
var Parent = function (name) {
  this.name = name;
  this.test = function () {
    console.log("parent test");
  }
  this.testOverride = function () {
    console.log("parent testOverride");
  }
}
// define a function extend
Parent.prototype.extend = function () {
  // parent properties to override or add
  var override = arguments[0];
  return function () {
    Parent.apply(this, arguments);
    // add and override properties
    Object.keys(override).forEach(el =>{
      this[el] = override[el];
    })
   }
}
// create a Child from the Parent and override
// the function "testOverride" and keep "test" unchanged
var Child = Parent.prototype.extend({
  y: 10,
  testOverride: function () { 
    console.log("child testOverride"); 
  }
});
// Create an object of type Parent
var p = new Parent("Parent");
// Create an object of type Child
var c = new Child("Child");
console.log(p.name);
// Parent
console.log(c.name);
// Child
p.test();
//parent test
p.testOverride();
//parent testOverride
c.test();
//parent test
c.testOverride();
//child testOverride
person phoenixstudio    schedule 04.07.2021

Это заставит расширять ваши свойства, создавая новый объект с прототипами параметров объекта без изменения переданного объекта.

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();
}

Но если вы хотите расширить свой объект, не изменяя его параметры, вы можете добавить 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
        }
    }
person Ndifreke    schedule 19.01.2018