在Javascript中扩展对象

时间:2012-05-03 11:18:40

标签: javascript function object prototype extends

我目前正在从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(...);

但我不知道如何使这个系统工作。如果不可能,请告诉我另一个扩展对象的替代方案。

17 个答案:

答案 0 :(得分:187)

修改
在使用该代码之前,请查看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

答案 1 :(得分:80)

没有"新"关键字。

使用Object.create()更简单的语法。

我认为Javascript应该尝试在没有" new"的情况下进入营地。它是一种无类别的语言,它不需要构造函数。您只需创建对象,然后扩展或变形它们。当然,有一些陷阱,但这更加强大和简单:

// 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 doesn't affect `Person` prototypes
skywalker.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'

进一步阅读

** 10月18日更新。采用ES6语法并使用constlet。添加了示例,说明如何使属性不可变。

** 1月17日更新。包括ES6 Object.assign()。

正如您所看到的,分配需要多个语句。使用ES6,您可以使用#assign方法缩短分配。 (要在旧版浏览器上使用polyfill,请参阅MDN on 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'

这种模式有可能会绊倒"经典训练的"程序员。尽管如此,我发现这种模式更具可读性。

答案 2 :(得分:51)

如果您尚未找到方法,请使用JavaScript对象的associative属性将扩展函数添加到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);

答案 3 :(得分:28)

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

答案 4 :(得分:18)

另一年后,我可以告诉你还有另一个不错的答案。

如果您不喜欢原型设计的工作方式以扩展对象/类,请参考: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"

答案 5 :(得分:13)

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

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

有关详情,请参阅链接

MDN - Object.assign()

如果您需要 Polyfill for ES5 ,该链接也会提供。 :)

答案 6 :(得分:8)

您可能需要考虑使用underscore.js之类的辅助库,其中包含it's own implementation of extend()

通过查看源代码,这也是一种很好的学习方式。 annotated source code page非常有用。

答案 7 :(得分:6)

仍在努力寻求简单,最佳方法的人们,可以使用 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的名称为 Robo

答案 8 :(得分:6)

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(谷歌浏览器/火狐) - 2015年3月夜间版本中使用。

答案 9 :(得分:3)

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

还有纯javascript实现,这是ECMAscript 6的一部分: Object.assign https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

答案 10 :(得分:2)

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年的答案,因为自从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

答案 11 :(得分:1)

摘要:

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

答案 12 :(得分:0)

您只需使用以下命令即可:

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
}

答案 13 :(得分:0)

请为DOWNVOTE添加理由

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

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

修改特定的实例/对象

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

答案 14 :(得分:0)

虽然这项工作并非 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

答案 15 :(得分:-1)

这将扩展您的属性,使用对象参数原型创建一个新的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,可以将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
        }
    }

答案 16 :(得分:-2)

原型设计是一种很好的方式,但原型有时非常危险并且可能导致错误。我更喜欢将它封装到一个基础对象中,就像Ember.js对它的Ember.Object.extend和Ember.Object.reopen一样。使用起来更加安全。

我创建了一个关于如何设置类似与Ember.Object使用的内容的要点。

这里是链接:https://gist.github.com/WebCloud/cbfe2d848c80d4b9e9bd