2015-11-13 2 views
15

Обычно я реализую наследование по следующим строкам.Использование Object.assign и Object.create для наследования

function Animal() { this.x = 0; this.y = 0;} 

Animal.prototype.locate = function() { 
    console.log(this.x, this.y); 
    return this; 
}; 
Animal.prototype.move = function(x, y) { 
    this.x = this.x + x; 
    this.y = this.y + y; 
    return this; 
} 


function Duck() { 
    Animal.call(this); 
} 

Duck.prototype = new Animal(); 
Duck.prototype.constructor = Duck; 
Duck.prototype.speak = function() { 
    console.log("quack"); 
    return this; 
} 

var daffy = new Duck(); 

daffy.move(6, 7).locate().speak(); 

Я прочитал this post by Eric Elliott и, если я правильно понимаю, я могу использовать Object.create и Object.assign вместо? Неужели это так просто?

var animal = { 
    x : 0, 
    y : 0, 
    locate : function() { 
    console.log(this.x, this.y); 
    return this; 
    }, 
    move : function (x, y) { 
    this.x = this.x + x; 
    this.y = this.y + y; 
    return this; 
    } 
} 

var duck = function() { 
    return Object.assign(Object.create(animal), { 
    speak : function() { 
     console.log("quack"); 
     return this; 
    } 
    }); 
} 

var daffy = duck(); 

daffy.move(6, 7).locate().speak(); 

Как в стороне, функции конструктора условных обозначений капитализируются, должны ли объектные литералы, которые действуют как конструкторы, также капитализируются?

Я понимаю, что есть много вопросов здесь обсуждают new против Object.create, но они, как правило, кажется, относятся к Duck.prototype = new Animal(); против Duck.prototype = Object.create(Animal.prototype);

+2

Если вы используете ES2015, вы должны использовать 'class' &' extend' – Amit

+7

@ Признаться, это, очевидно, не вопрос, который он задал. Является ли использование 'class' и' extend' хорошей идеей, является частью другого обсуждения. – nils

+1

@Amit -Почему я должен использовать 'class' и' extend'? Если я это сделаю, мне придется использовать «новый», не так ли? Какое преимущество имеет использование 'class' и' extend' над 'Object.assign (Object.create (...'? – user5325596

ответ

16

Да, это так просто. В вашем примере с Object.create/Object.assign вы используете заводскую функцию для создания новых экземпляров duck (аналогично тому, как jQuery создает новые экземпляры, если вы выбираете элемент с var body = $('body')). Преимущество этого стиля кода заключается в том, что он не заставляет вас вызывать конструктор animal, если вы хотите создать новый экземпляр duck (в отличие от классов ES2015).

Различие в инициализации

Может быть один интересный факт, который работает несколько иначе, чем если бы вы были использовать конструктор (или любую другую функцию инициализации):

При создании duck instace, всех свойства animal находятся в слоте [[Prototype]] экземпляра duck.

var daffy = duck(); 
console.log(daffy); // Object { speak: function() } 

Так daffy не имеет собственных x и y свойства еще. Однако, когда вы вызываете следующее, они будут добавлены:

daffy.move(6, 7); 
console.log(daffy); // Object { speak: function(), x: 6, y: 7 } 

Почему? В функции-теле animal.move, мы имеем следующее заявление:

this.x = this.x + x; 

Итак, когда вы называете это с daffy.move, this относится к daffy. Поэтому он попытается присвоить this.x + xthis.x. Since this.x еще не определено, цепь [[Prototype]]daffy перемещена вниз к animal, где animal.x.

Таким образом, при первом вызове this.x с правой стороны присваивания относится к animal.x, потому что daffy.x не определен. Второй раз daffy.move(1,2) называется, this.x с правой стороны будет daffy.x.

Альтернативный Синтаксис

В качестве альтернативы, вы можете также использовать Object.setPrototypeOf вместо Object.create/Object.assign (OLOO Style):

var duck = function() { 
    var duckObject = { 
     speak : function() { 
      console.log("quack"); 
      return this; 
     } 
    }; 
    return Object.setPrototypeOf(duckObject, animal); 
} 

соглашения об именах

Я не в курсе каких-либо установленных конвенций , Кайл Симпсон использует заглавные буквы в OLOO, Эрик Эллиот, похоже, использует строчные буквы. Лично я бы придерживался нижнего регистра, потому что объектные литералы, которые выступают в качестве конструкторов, уже являются полностью полноценными объектами (а не только планом, например, классами).

Singleton

Если вы только хотел один экземпляр (например, для одноточечного), вы могли бы просто назвать его прямо:

var duck = Object.assign(Object.create(animal), { 
    speak : function() { 
     console.log("quack"); 
     return this; 
    } 
}); 

duck.move(6, 7).locate().speak(); 
+0

Спасибо за длинный ответ. Я полагаю, что преимущество 'Object.assign (Object.create' над' setPrototypeOf' заключается в том, что я могу сделать более простое «множественное наследование» (из-за отсутствия лучшего термина), например 'Object.assign (Object.create (animal) , duckObj, someOtherObj, anotherObj) '? Существуют ли какие-либо недостатки для' Object.присваивать (Object.create'? – user5325596

+0

'Object.assign (Object.create (animal), duckObj, someOtherObj, anotherObj)' больше смешивается с прототипами mixin :) Полученный объект будет содержать собственные свойства duckObj, someOtherObj и anotherObj и будет иметь только анимал в слоте [[Prototype]]. – nils

+0

Если вы хотите иметь несколько объектов в цепочке прототипов, вам нужно будет их смешать в 'Object.create':' Object.assign (Object.create ({animal: animal, bird: bird}), утка); 'или' Object.setPrototypeOf (duck, {animal: animal, bird: bird}); ' – nils

0

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

Верхний пример - прототипный и нижний, функционально разделенный. Проверьте эту ссылку: JS Instantiation patterns

Кроме того, это не связано с es6.

+0

Это не касается его примера. И да, 'Object.assign' - ES6. – nils

+0

Не знаете, почему вы говорите «Кроме того, это не связано с es6». 'Object.assign' является частью стандарта ES6. – user5325596

+0

ES6 как новые стандарты для того, что вы по существу используете как «классы». Опубликованный код специально связан с ES5. – Shinobi881

9

Я прочитал this post by Eric Elliott и если я понимаю, правильно я могу использовать Object.create и Object.assign вместо этого? Неужели это так просто?

Да, create и assign гораздо проще, потому что они примитивы, и меньше магии происходит - все, что вы делаете явно.

Однако пример Eric's mouse немного запутан, поскольку он оставляет один шаг и смешивает наследство мышей от животных с экземплярами мыши.

Скорее давайте попробуем расшифровывать ваш пример утенок снова - давайте начнем с делать это буквально:

const animal = { 
    constructor() { 
    this.x = 0; 
    this.y = 0; 
    return this; 
    }, 
    locate() { 
    console.log(this.x, this.y); 
    return this; 
    }, 
    move(x, y) { 
    this.x += x; 
    this.y += y; 
    return this; 
    } 
}; 
const duck = Object.assign(Object.create(animal), { 
    constructor() { 
    return animal.constructor.call(this); 
    }, 
    speak() { 
    console.log("quack"); 
    return this; 
    } 
}); 
/* alternatively: 
const duck = Object.setPrototypeOf({ 
    constructor() { 
    return super.constructor(); // super doesn't work with `Object.assign` 
    }, 
    speak() { … } 
}, animal); */ 

let daffy = Object.create(duck).constructor(); 
daffy.move(6, 7).locate().speak(); 

Вы должны увидеть, что здесь происходит на самом деле не отличается от использования конструкторов (или class синтаксис для этого вопроса), мы просто сохранили наши прототипы непосредственно в переменных, и мы создаем экземпляр с явным вызовом create и constructor.

Теперь вы можете понять, что наша duck.constructor ничего не делает, но называть его супер метод, так что мы можем на самом деле опустить его полностью, и пусть наследование делать свою работу:

const duck = Object.assign(Object.create(animal), { 
    speak() { 
    console.log("quack"); 
    return this; 
    } 
}); 

Другая вещь, которая часто меняется является инициализация свойств экземпляра. Там фактически нет оснований инициализирует их, если мы на самом деле не нужны, это достаточно, чтобы поставить некоторые значения по умолчанию на прототипе:

const animal = { 
    x: 0, 
    y: 0, 
    locate() { 
    console.log(this.x, this.y); 
    } 
}; 
const duck = … Object.create(animal) …; 

let daffy = Object.create(duck); // no constructor call any more! 
daffy.x = 5; // instance initialisation by explicit assignment 
daffy.locate(); 

Проблема с этим состоит в том, что it only works for primitive values, и это становится повторяющимся.Это где фабричные функции получить в:

function makeDuck(x, y) { 
    return Object.assign(Object.create(duck), {x, y}); 
} 
let daffy = makeDuck(5, 0); 

Для обеспечения простого наследования, инициализация часто не делается на заводе, но в выделенном методе, поэтому он может быть вызван «подклассов» экземпляров, а также. Вы можете назвать этот метод init, или вы можете называть его constructor, как я и сделал выше, это в основном то же самое.

Как в стороне, функции конструктора условных обозначений капитализируются, должны ли объектные литералы, которые действуют как конструкторы, также капитализируются?

Если вы не используете какие-либо конструкторы, вы можете присвоить новое значение именам заглавных переменных, да. Однако это может смутить всех, кто не привык к этому. И, кстати, они не являются «объектными литералами, которые действуют как конструкторы», они всего лишь прототипов объектов.

+0

это лучший ответ. Спасибо! – Sulliwane

+0

уже сделано. Должен быть принятый ответ :) – Sulliwane

+0

«Я прочитал этот пост Эрика Эллиотта», где вы останавливаете и игнорируете все. Никто не знает меньше о хорошем дизайне, чем этот громкий простак. Все может быть выполнено с помощью «нового» и «вызова» и «object.create» - последнего из которых только для наследования. Вышеприведенный пример настолько перезаписан, что является исключительным доказательством эффекта Крюгера Даннинга. К счастью, ES2015 рационально добавил расширенные ключевые слова и ключевые слова. – Hal50000

Смежные вопросы