在 Javascript 中扩展对象

我目前正在从 Java转换到 Javascript,对我来说,弄清楚如何以我想要的方式扩展对象有点困难。

我在网上看到一些人使用一种叫做扩展对象的方法,代码如下:

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

但我不知道该如何运作这个系统。如果这是不可能的,请告诉我另一个选择,扩展对象。

350844 次浏览

您希望从 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

您可能需要考虑使用类似于 下划线 js的 helper 库,它具有 它自己的 extend()实现

通过查看它的源代码也是一个很好的学习方法。

如果你还没有找到一个方法,使用 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);

又过了一年,我可以告诉你,还有一个不错的答案。

如果您不喜欢为了在对象/类上进行扩展而进行原型开发的方式,可以看看这个: 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"

不同的方法: 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文档。

用 Object.create ()实现更简单的“散文式”语法

以及 Javascript 真正的原型性质

* 此示例针对 ES6类和 TypeScript 进行了更新。

首先,Javascript 是 原型语言 ,而不是基于类的。它的真正本质表现在下面的原型形式中,你可能会看到它非常简单,像散文一样,但是很强大。

极低密度辐射;

Javascript

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

打字机

在 TypeScript 中,需要设置接口,在创建 Person原型的后代时将扩展这些接口。一个突变的 politeGreet展示了一个在后代 jack上附加新方法的例子。

interface IPerson extends Object {
name: string
greet(): void
}


const Person: IPerson =  {
name:  'Anonymous',
greet() {
console.log(`Hi, I am ${this.name}.`)
}
}


interface IPolitePerson extends IPerson {
politeGreet: (title: 'Sir' | 'Mdm') => void
}


const PolitePerson: IPolitePerson = Object.create(Person)
PolitePerson.politeGreet = function(title: string) {
console.log(`Dear ${title}! I am ${this.name}.`)
}


const jack: IPolitePerson = Object.create(Person)
jack.name = 'Jack'
jack.politeGreet = function(title): void {
console.log(`Dear ${title}! I am ${this.name}.`)
}


jack.greet()  // "Hi, I am Jack."
jack.politeGreet('Sir') // "Dear Sir, I am Jack."

这就免除了有时令人费解的构造函数模式。一个新对象从旧对象继承而来,但是能够拥有自己的属性。如果我们试图从新对象(#greet())中获取新对象 jack所缺少的成员,旧对象 Person将提供该成员。

道格拉斯·克罗克福特的话: “对象从对象继承。还有什么比这更面向对象的呢?”

您不需要构造函数,也不需要 new实例化。您只需创建对象,然后对它们进行扩展或变形。

此模式还提供 不变性(部分或全部)获得者/接收者

简洁明了。它的简单性不会损害特性。继续读下去。

创建 Person prototype的后代/副本(技术上比 class更正确)。

* 注: 以下例子载于 JS。要编写 Type 脚本,只需按照上面的示例设置用于键入的接口。

const Skywalker = Object.create(Person)
Skywalker.lastName = 'Skywalker'
Skywalker.firstName = ''
Skywalker.type = 'human'
Skywalker.greet = function() { console.log(`Hi, my name is ${this.firstName} ${this.lastName} and I am a ${this.type}.`


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

当你从 Person原型分支 Robot后代时,你不会影响 Skywalkeranakin:

// 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(): void
}
const Robot: Robot = Object.create(Person)
Robot.machineGreet = function() { 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课程

与使用 Object 相比,它更笨重,但代码的可读性还不错:

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

进一步阅读

可写性,可配置性和免费获取器和设置器!

对于自由的 getter 和 setter,或者额外的配置,您可以使用 Object.create ()的第二个参数 a.k.a properties tiesObject。它也可在 对象。定义属性对象。定义属性

为了说明它的有用性,假设我们希望所有的 Robot都严格由金属制成(通过 writable: false) ,并标准化 powerConsumption值(通过 getter 和 setter)。


// Add interface for Typescript, omit for Javascript
interface Robot extends Person {
madeOf: 'metal'
powerConsumption: string
}


// add `: Robot` for TypeScript, omit for Javascript.
const Robot: Robot = Object.create(Person, {
// define your property attributes
madeOf: {
value: "metal",
writable: false,  // defaults to false. this assignment is redundant, and for verbosity only.
configurable: false, // defaults to false. this assignment is redundant, and for verbosity only.
enumerable: true  // defaults to false
},
// 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.')
}
}
})


// add `: Robot` for TypeScript, omit for Javascript.
const newRobot: Robot = 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'
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

2017年1月更新:

请忽略我2015年的回答,因为从 ES6(Ecmasctipt6)开始,Javascript 现在支持 extends关键字

- 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

在大多数项目中,都有一些对象扩展的实现: 下划线、 jquery、 loash: 延伸

还有纯粹的 javascript 实现,这是 ECMAscript 6: 对象. 分配的一部分: Https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/object/assign

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夜间版本中获得。

在 ES6 中,可以使用类似于

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

注意 Object.sign ()触发器 setter,而传播语法则不是。

更多信息见链接,MDN 扩展语法


老答案:

在 ES6 中,有用于复制属性值的 Object.assign。如果不想修改目标对象(传递的第一个参数) ,请使用 {}作为第一个参数。

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

有关详细信息,请参阅链接,MDN-Object.sign ()

如果您需要的是一个 ES5填充料,链接也提供它。 :)

你可以简单地使用:

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
}

请加上拒绝投票的理由

  • 不需要使用任何外部库来扩展

  • 在 JavaScript 中,所有内容都是一个对象(除了三个 基本数据类型,甚至它们也自动包装为 此外,所有对象都是可变的。

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

修改特定的 instance/object

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

多亏了 Ross Harmes 和 Dustin Diaz

这将使扩展属性创建一个带有对象参数原型的新 Object,而无需更改传递的对象。

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

但是,如果希望在不修改 Object 参数的情况下扩展 Object,则可以将 extendProperty 添加到对象中。

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

摘要:

Javascript 使用一种称为 原型遗传的机制。在查找对象上的属性时使用原型继承。当我们在 javascript 中扩展属性时,我们是从一个实际的对象继承这些属性。它的工作方式如下:

  1. 当请求一个对象属性时,(例如 myObj.foomyObj['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. 显式设置原型属性

当使用构造函数创建对象时,我们可以将 add 属性设置为其原型对象属性。使用 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));

人们还在为简单和最好的方法而奋斗,你可以用 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的名称为 机器人

虽然这项工作是不是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

简单易读的解决方案是使用扩展运算符

...

例如:

const obj1 = {a: "a"} const obj2 = {b: "b"} const result = {...obj1, ..obj2,} console.log("result", result) // must be {a: "a", b: "b"}