Расширение объекта в 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(...);

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

Ответ 1

Edit:
Перед использованием кода проверьте комментарии пользователя user2491400, в котором сообщается о побочных эффектах просто присваивания prototype.

Оригинальный ответ:

Вы хотите "наследовать" от объекта прототипа 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.name = name;
  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

Ответ 2

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

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

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

// base 'Person' prototype
const Person = {
   name : '',
   age  : 22,
   type : 'human',
   greet() {
       console.log('Hi, my name is ' + this.name + ' and I am a ' + this.type + '.' )
   }
}

// create an instance of 'Person':
const skywalker = Object.create(Person)
skywalker.name = 'Anakin Skywalker'
skywalker.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'

Расширение базового прототипа

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

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

Еще один уровень глубже

// create a new instance 'Robot'
const Astromech = Object.create(Robot)
Astromech.variant = 'astromech'

const r2d2 = Object.create(Astromech)
r2d2.name = 'R2D2'
r2d2.greet() // '0000111010101011100111....'

// morphing the 'Robot' object does not affect 'Person' prototypes
skywalker.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'

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

** Обновление 3 октября 18. Принять синтаксис ES6 и использовать const и let. Добавлен пример, показывающий, как сделать свойства неизменяемыми.

** Обновление 22 января. Включен ES6 Object.assign().

Как видите, назначение требует нескольких операторов. В ES6 вы можете использовать метод #assign для сокращения назначений. (Для использования полифилла в старых браузерах см. MDN на ES6.)

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

//you can do this
const Robot = Object.create(Person)
Object.assign(Robot, {
    name: "Robot",
    madeOf: "metal",
    powerConsumption_kWh: 5,
    fullCharge_kWh: 10,
    currentCharge_kWh: 5
})

//attach some methods unique to Robot prototype.
Robot.charge = function(kWh) {
    let self = this
    this.currentCharge_kWh = Math.min(self.fullCharge_kWh, self.currentCharge_kWh + kWh)
    var percentageCharged = this.currentCharge_kWh / this.fullCharge_kWh * 100
    console.log(this.name + (percentageCharged === 100) ? ' is fully charged.' : ' is ' + percentageCharged +'% charged.')
}

Robot.charge(5) // outputs "Robot is fully charged."

Вы также можете использовать второй аргумент Object.create() a.k.a propertiesObject, который я считаю слишком длинным. Единственная причина использовать его вместо #assign - если вам нужен больший контроль над значениями, т.е. возможностью записи/конфигурирования и т.д. Обратите внимание, что Robot строго должен быть полностью металлическим.

const Robot = Object.create(Person, {
    madeOf: { 
        value: "metal",
        writable: false,
        configurable: false,
        enumerable: true
    },
    powerConsumption: {
        value: "5kWh",
        writable: true,
        configurable: true,
        enumerable: true   
    }
})

И все прототипы Robot не могут быть сделаны из чего-то другого.

const polymerRobot = Object.create(Robot)

polymerRobot.madeOf = 'polymer'

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

В этом паттерне есть ошибки, которые могут запутать "классически обученных" программистов. Тем не менее, я нахожу этот шаблон гораздо более читабельным.

Ответ 3

Если вы еще не определили способ, используйте ассоциативное свойство объектов 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);

Ответ 4

Другой подход: 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 prototype to Person 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.

Ответ 5

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

Если вам не нравится, как работает прототипирование, чтобы расширить объекты/классы, сделайте следующее: 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"

Ответ 6

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

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

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

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


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

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

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

Подробнее см. по ссылке, MDN - Object.assign()

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

Ответ 7

Люди, которые все еще борются за простой и лучший подход, вы можете использовать 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);

Ответ 9

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 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.

Ответ 11

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

Ответ 12

Резюме:

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

Ответ 13

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

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

update: Я проверил для 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
}

Ответ 14

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

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

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

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

Modify a specific instance/object.

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

Modify the class

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*/

благодаря Россу Хармесу, Дастину Диасу

Ответ 15

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

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

Но если вы хотите расширить свой Объект без изменения его параметров, вы можете добавить exteProperty к своему объекту.

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

Ответ 16

Прототипирование - это хороший способ, но прототип иногда довольно опасен и может привести к ошибкам. Я предпочитаю инкапсулировать это в базовый объект, например, Ember.js делает это Ember.Object.extend и Ember.Object.reopen. Это гораздо безопаснее использовать.

Я создал суть с тем, как вы настроили что-то похожее на то, что использует Ember.Object.

Здесь ссылка: https://gist.github.com/WebCloud/cbfe2d848c80d4b9e9bd