178

私は現在からJavaに変換しJavascriptていますが、オブジェクトを希望どおりに拡張する方法を理解するのは少し難しいです。

インターネット上の何人かの人々がextendonobjectと呼ばれるメソッドを使用しているのを見てきました。コードは次のようになります。

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

しかし、このシステムを機能させる方法がわかりません。それが不可能な場合は、オブジェクトを拡張する別の方法を教えてください。

4

17 に答える 17

198

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
于 2012-05-03T11:55:08.443 に答える
122

Object.create() を使用したより単純な「散文のような」構文

そして、Javascript の真の原型的性質

*この例は、ES6 クラスと TypeScript 用に更新されています。

まず、Javascript はプロトタイプ言語であり、クラスベースではありません。その真の性質は、以下のプロトタイプ形式で表現されています。これは、非常に単純で、散文のようでありながら強力であることがわかるでしょう。

TLDR;

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

PersonTypeScript では、プロトタイプの子孫を作成するときに拡張されるインターフェイスを設定する必要があります。ミューテーションpoliteGreetは、子孫に新しいメソッドを追加する例を示していjackます。

interface Person {
    name: string
    greet(): void
}

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

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

const jack: jack = Object.create(Person)
jack.name = 'Jack'
jack.politeGreet = function(title) {
    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がメンバーを提供します。

Douglas Crockford の言葉を借りれば、「オブジェクトはオブジェクトから継承されます。これ以上のオブジェクト指向はありません。」

newコンストラクターもインスタンス化も必要ありません。オブジェクトを作成し、それらを拡張またはモーフィングするだけです。

このパターンは、不変性 (部分的または完全)ゲッター/セッターも提供します。

クリーンでクリアな。そのシンプルさは機能を妥協しません。読む。

Person の子孫/コピーを作成しますprototype(技術的には よりも正確ですclass)。

*注: 以下の例は JS です。Typescript で書き込むには、上記の例に従って、入力用のインターフェイスをセットアップします。

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

直接代入の代わりにコンストラクターを破棄するのが安全でないと感じる場合、一般的な方法の 1 つは、#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

Robotプロトタイプから子孫を分岐する場合、影響はありPersonません。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

PersonTypeScript では、インターフェイスを拡張する必要もあります。

interface Robot extends Person {
    machineGreet(): void
}
const Robot: Robot = Object.create(Person)
Robot.machineGreet = function() { console.log(101010) }

そして Mixins を持つこともできます -- なぜなら.. Darth Vader は人間ですか、それともロボットですか?

const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped because you get the point by now.
Object.assign(darthVader, Robot)

Darth Vader は次のメソッドを取得します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 クラス

オブジェクトを使用する場合に比べて扱いにくいですが、コードの読みやすさは問題ありません。

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

参考文献

書き込み可能性、構成可能性、および無料のゲッターとセッター!

無料のゲッターとセッター、または追加の構成については、Object.create() の 2 番目の引数、別名 propertiesObject を使用できます。#Object.definePropertyおよび# Object.definePropertiesでも​​使用できます。

その有用性を説明するために、すべてRobotを厳密に金属で作成し ( を使用writable: false)、値を標準化powerConsumptionする (ゲッターとセッターを使用) とします。


// 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'
于 2015-02-02T16:08:18.567 に答える
51

まだ方法がわからない場合は、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);
于 2013-11-04T20:38:55.283 に答える
37

ES6では、次のようなスプレッド演算子を使用できます

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

Object.assign() はセッターをトリガーしますが、スプレッド構文はトリガーしません。

詳細については、リンクを参照してください。MDN -Spread Syntax


古い答え:

ES6Object.assignでは、プロパティ値をコピーするためのものがあります。{}ターゲット オブジェクト (渡された最初のパラメーター) を変更したくない場合は、最初のパラメーターとして使用します。

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

詳細については、リンク、MDN - Object.assign()を参照してください。

ES5 の Polyfillが必要な場合は、リンクからも提供されます。:)

于 2016-01-15T08:33:14.450 に答える
31

別のアプローチ: 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のドキュメントも確認してください。

于 2014-08-04T16:42:37.897 に答える
14

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という名前が入ります。

于 2018-10-08T11:02:53.667 に答える
8

の独自の実装を持つunderscore.jsなどのヘルパー ライブラリの使用を検討することをお勧めします。extend()

また、ソース コードを見て学習するのも良い方法です。注釈付きのソース コード ページは非常に便利です。

于 2012-05-03T12:45:39.983 に答える
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 (Google Chrome / Firefox) - 2015 年 3 月のナイトリー ビルドで利用できます。

于 2015-04-09T11:30:26.717 に答える
4

プロジェクトの大部分では、オブジェクト拡張の実装がいくつかあります: アンダースコア、jquery、lodash: extend

ECMAscript 6 の一部である純粋な JavaScript 実装もあります: Object.assign : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

于 2015-03-14T18:26:40.020 に答える
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

2017 年 1 月更新:

extendsJavascript はES6 (Ecmasctipt6) 以降のキーワードをサポートするようになったため、2015 年の私の回答は無視してください。

- 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
于 2015-02-19T06:43:08.563 に答える
0

反対票を投じる理由を追加してください

  • 外部ライブラリを使用して拡張する必要はありません

  • JavaScript では、すべてがオブジェクトです (3 つのプリミティブ データ型を除き、必要に応じてオブジェクトで自動的にラップされます)。さらに、すべてのオブジェクトは変更可能です。

JavaScript のクラス Person

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

ロス・ハームズ、ダスティン・ディアスに感謝

于 2016-04-20T14:24:07.203 に答える
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
}
于 2016-02-23T13:36:00.600 に答える