Расширение объекта в 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
В ответе @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);
Ответ 8
Возможно, вы захотите использовать вспомогательную библиотеку, например underscore.js, в которой имеет собственную реализацию extend()
.
И это также хороший способ узнать, посмотрев на него исходный код. аннотированная страница исходного кода весьма полезна.
Ответ 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.
Ответ 10
В большинстве проектов существует некоторая реализация расширения объекта: подчеркивание, jquery, lodash: расширение.
Существует также чистая реализация javascript, которая является частью ECMAscript 6: Object.assign:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Ответ 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, мы наследуем эти свойства от реального объекта. Он работает следующим образом:
- Когда запрашивается свойство объекта (например,
myObj.foo
или myObj['foo']
), механизм JS сначала будет искать это свойство в самом объекте
- Когда это свойство не найдено в самом объекте, оно поднимется по цепочке прототипов и посмотрит на объект-прототип. Если это свойство также не найдено здесь, оно будет продолжать подниматься по цепочке прототипов, пока свойство не будет найдено. Если свойство не найдено, оно выдаст ошибку ссылки.
Когда мы хотим расшириться от объекта в 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