Mở rộng một đối tượng trong Javascript


164

Tôi hiện đang chuyển đổi từ Java sang Javascript và tôi hơi khó để tìm ra cách mở rộng các đối tượng theo cách tôi muốn.

Tôi đã thấy một số người trên internet sử dụng một phương pháp gọi là mở rộng trên đối tượng. Mã sẽ trông như thế này:

var Person = {
   name : 'Blank',
   age  : 22
}

var Robot = Person.extend({
   name : 'Robo',
   age  : 4
)}

var robot = new Robot();
alert(robot.name); //Should return 'Robo'

Có ai biết làm thế nào để làm việc này? Tôi đã nghe nói rằng bạn cần phải viết

Object.prototype.extend = function(...);

Nhưng tôi không biết làm thế nào để hệ thống này hoạt động. Nếu không thể, xin vui lòng chỉ cho tôi một phương án khác mở rộng một đối tượng.


trả lại đúng sự thật; nhưng đó là lý do tại sao tôi hỏi :)
Wituz

2
tôi sẽ đề nghị đi qua tuutorial tuyệt đẹp này trên MDN: - developer.mozilla.org/en/iêu
Pranav

Nếu sau khi đọc những tài liệu hay mà bạn vẫn tò mò về một extendchức năng, tôi đã thiết lập một ví dụ ở đây: jsfiddle.net/k9LRd
Codrin Eugeniu

2
Tôi cũng khuyên bạn không nên suy nghĩ nghiêm túc về việc 'chuyển đổi từ Java sang JavaScript' và hơn thế nữa là 'học một ngôn ngữ mới, Javascript, có cú pháp tương tự như Java'
Toni Leigh

Câu trả lời:


195

Bạn muốn 'kế thừa' từ đối tượng nguyên mẫu của 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

4
Tôi có một câu hỏi: làm thế nào là Person()constructor được gọi khi bạn làm new Robot()? Dường như với tôi rằng bạn nên gọi hàm tạo của lớp cơ sở đó thay vì thực hiện this.name = name;trong hàm Robot()tạo ...
Alexis Wilke

21
@AlexisWilke: Đúng, bạn nên gọi Person.apply(this, arguments);. Nó cũng sẽ được sử dụng tốt hơn Robot.prototype = Object.create(Person.prototype);thay vì new Person();.
Felix Kling

18
Như đã nêu của Felix, 'Robot.prototype = Person.prototype;' là một ý tưởng tồi nếu bất cứ ai muốn loại 'Robot' có phiên bản nguyên mẫu của riêng mình. Thêm các chức năng cụ thể của Robot mới cũng sẽ thêm nó vào người.
James Wilkins

20
Ví dụ này là hoàn toàn sai. Bằng cách đó, bạn thay đổi nguyên mẫu của Person. Đó không phải là sự kế thừa và bạn có nguy cơ đặt một mớ hỗn độn lớn trong lớp Người. Xem câu trả lời khuyến nghị sử dụng Object.create (). Đó là cách chính xác để làm việc.
nicolas-van

6
@osahyoun câu trả lời này có thứ hạng cao trong tìm kiếm của google. Tôi thực sự sẽ đề nghị bạn sửa mã và sửa chuỗi nguyên mẫu theo đề xuất của các bình luận khác ở đây.
raphaëλ

102

Thế giới không có từ khóa "mới".

Và cú pháp "giống như văn xuôi" đơn giản hơn với Object.create ().

* Ví dụ này được cập nhật cho các lớp ES6.

Trước hết, hãy nhớ rằng Javascript là một ngôn ngữ nguyên mẫu . Nó không dựa trên lớp học. Do đó, viết ở dạng nguyên mẫu cho thấy bản chất thực sự của nó, và có thể rất đơn giản, giống như văn xuôi và mạnh mẽ.

TLDR;

const Person = { name: 'Anonymous' } // person has a name

const jack = Object.create(Person)   // jack is a person
jack.name = 'Jack'                   // and has a name 'Jack'

Không, bạn không cần nhà xây dựng, không cần newkhởi tạo ( đọc lý do tại sao bạn không nên sử dụngnew ), không super, không hài hước buồn cười __construct. Bạn chỉ cần tạo Đối tượng và sau đó mở rộng hoặc biến đổi chúng.

( Nếu bạn biết về getters và setters, hãy xem phần "Đọc thêm" để xem cách mẫu này cung cấp cho bạn getters và setters miễn phí theo cách mà Javascript ban đầu dự định và sức mạnh của chúng .)

Cú pháp giống như văn xuôi: Base protoype

const Person = {

   //attributes
   firstName : 'Anonymous', 
   lastName: 'Anonymous',
   birthYear  : 0,
   type : 'human',

   //methods
   name() { return this.firstName + ' ' + this.lastName },
   greet() {
       console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' )
   },
   age() {
      // age is a function of birth time.
   }
}

const person = Object.create(Person). // that's it!

Nhìn thoáng qua, trông rất dễ đọc.

Mở rộng, tạo ra một hậu duệ của Person

* Các điều khoản chính xác là prototypes, và của họ descendants. Không có classes, và không cần instances.

const Skywalker = Object.create(Person)
Skywalker.lastName = 'Skywalker'

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

Một cách để cung cấp một cách "mặc định" để tạo một descendant, là bằng cách đính kèm một #createphương thức:

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

Những cách dưới đây có khả năng đọc thấp hơn:

So sánh với tương đương "cổ điển":

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

Person.isPrototypeOf(anakin) // returns false!
Skywalker.isPrototypeOf(anakin) // returns false!

Khả năng đọc mã bằng cách sử dụng kiểu "cổ điển" là không tốt.

Lớp ES6

Phải thừa nhận rằng, một số vấn đề trong số này đã bị các lớp ES6 xóa bỏ, nhưng vẫn:

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

Phân nhánh nguyên mẫu cơ sở

// create a `Robot` prototype by extending the `Person` prototype:
const Robot = Object.create(Person)
Robot.type = 'robot'
Robot.variant = '' // add properties for Robot prototype

Đính kèm các phương thức duy nhất để Robot

// Robots speak in binaries, so we need a different greet function:
Robot.machineGreet = function() { /*some function to convert strings to binary */ }

// morphing the `Robot` object doesn't affect `Person` prototypes
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
anakin.machineGreet() // error

Kiểm tra thừa kế

Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false

Bạn đã có mọi thứ bạn cần rồi! Không xây dựng, không khởi tạo. Văn xuôi sạch sẽ, rõ ràng.

đọc thêm

Khả năng ghi, Cấu hình và Getters và Setters miễn phí!

Đối với getters và setters miễn phí hoặc cấu hình bổ sung, bạn có thể sử dụng đối số thứ hai của Object.create () aka propertyObject. Nó cũng có sẵn trong # Object.defineProperty# Object.defineProperIES .

Để minh họa mức độ mạnh mẽ của nó, giả sử chúng ta muốn tất cả Robotđược làm hoàn toàn bằng kim loại (thông qua writable: false) và chuẩn hóa powerConsumptioncác giá trị (thông qua getters và setters).

const Robot = Object.create(Person, {
    // define your property attributes
    madeOf: { 
        value: "metal",
        writable: false,
        configurable: false,
        enumerable: true
    },
    // getters and setters, how javascript had (naturally) intended.
    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.')
        }  
    }
})

const newRobot = Object.create(Robot)
newRobot.powerConsumption = '5MWh'
console.log(newRobot.powerConsumption) // outputs 5,000kWh

Và tất cả các nguyên mẫu Robotkhông thể là madeOfcái gì khác bởi vì writable: false.

const polymerRobot = Object.create(Robot)

polymerRobot.madeOf = 'polymer'

console.log(polymerRobot.madeOf) // outputs 'metal'

Mixins (sử dụng # Object.assign) - Anakin Skywalker

Bạn có thể cảm nhận được nơi này đang diễn ra ...?

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

Object.assign(darthVader, Robot)

Darth Vader có các phương thức Robot:

darthVader.greet() // inherited from `Person`, outputs "Hi, my name is Darth Vader..."
darthVader.machineGreet() // inherited from `Robot`, outputs 001010011010...

Cùng với những điều kỳ lạ khác:

console.log(darthVader.type) // outputs robot.
Robot.isPrototypeOf(darthVader) // returns false.
Person.isPrototypeOf(darthVader) // returns true.

Chà, dù Darth Vader là đàn ông hay máy móc thì thực sự chủ quan:

"Bây giờ anh ta là cỗ máy nhiều hơn con người, xoắn và ác." - Obi-Wan Kenobi

"Tôi biết có tốt trong bạn." - Luke Skywalker

Thêm - Cú pháp ngắn hơn một chút với # Object.assign

Trong tất cả khả năng, mẫu này rút ngắn cú pháp của bạn. Nhưng ES6 # Object.assign có thể rút ngắn thêm một số (Để polyfill sử dụng trên các trình duyệt cũ hơn, xem MDN trên ES6 ).

//instead of this
const Robot = Object.create(Person)
Robot.name = "Robot"
Robot.madeOf = "metal"

//you can do this
const Robot = Object.create(Person)
Object.assign(Robot, {
    name: "Robot",
    madeOf: "metal"
    // for brevity, you can imagine a long list will save more code.
})

8
có một upvote cho việc không sử dụng hàm xây dựng.
nsmark

1
Lập trình viên "được đào tạo cổ điển", ý của bạn là gì?
Petra

1
Tôi đến từ một tư duy OOP cổ điển và câu trả lời này đã giúp tôi rất nhiều. Hai câu hỏi về mã: 1) Ngày nay ES2015 có phải là Object.assign(Robot, {a:1}một lựa chọn tốt cho extend()phương pháp của bạn không? 2) Làm cách nào để ghi đè greet()phương thức để nó trả về cùng một văn bản, nhưng có thêm "ghi đè lời chào"?
Barry Staes

2
1) #Object.assignkhông looke như một sự thay thế tốt. Nhưng hỗ trợ trình duyệt thấp hơn atm. 2) Bạn sẽ sử dụng thuộc __proto__tính của đối tượng để truy cập chức năng chào của nguyên mẫu. sau đó bạn gọi hàm chào nguyên mẫu với phạm vi của callee được truyền vào. trong trường hợp này, hàm này là nhật ký giao diện điều khiển, do đó không thể "nối thêm". Nhưng với ví dụ này tôi nghĩ bạn có được sự trôi dạt. skywalker.greet = function() { this.__proto__.greet.call(this); console.log('a greet override'); }
Calvintwr

1
Vâng, đó là một cuộc thảo luận nên có với những người duy trì Đặc tả ngôn ngữ ECMAScript. Tôi thường đồng ý, nhưng tôi phải làm việc với những gì tôi có.

51

Nếu bạn chưa tìm ra cách nào, hãy sử dụng thuộc tính kết hợp của các đối tượng JavaScript để thêm hàm mở rộng vào Object.prototypenhư dưới đây.

Object.prototype.extend = function(obj) {
   for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
         this[i] = obj[i];
      }
   }
};

Sau đó bạn có thể sử dụng chức năng này như hiển thị bên dưới.

var o = { member: "some member" };
var x = { extension: "some extension" };

o.extend(x);

18
Coi chừng việc này sẽ tạo con trỏ tới đối tượng ban đầu trong lớp 'con' khi sử dụng các đối tượng / mảng trong lớp 'cha mẹ'. Để giải thích: Nếu bạn có một đối tượng hoặc mảng trong lớp cha của bạn, việc sửa đổi nó trong một lớp con mở rộng trên cơ sở đó, sẽ thực sự sửa đổi nó cho tất cả các lớp con mở rộng trên cùng lớp cơ sở này.
Harold

Harold, Cảm ơn đã làm nổi bật thực tế đó. Điều quan trọng đối với bất cứ ai sử dụng hàm để kết hợp một điều kiện kiểm tra các đối tượng / mảng và tạo các bản sao của chúng.
mai

30

Cách tiếp cận khác nhau: Object.create

Mỗi câu trả lời @osahyoun, tôi thấy sau đây là cách tốt hơn và hiệu quả hơn để 'kế thừa' từ đối tượng nguyên mẫu của 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;

Tạo các trường hợp mới:

var person = new Person("Bob");
var robot = new Robot("Boutros");

person.info(); // Name: Bob Type: human
robot.info();  // Name: Boutros Type: robot

Bây giờ, bằng cách sử dụng Object.create :

Person.prototype.constructor !== Robot

Kiểm tra tài liệu MDN .


2
Chỉ muốn nói @GaretClaborn nó hoạt động chính xác, nhưng bạn không chuyển nametham số cho hàm tạo mẹ, như thế này: jsfiddle.net/3brm0a7a/3 (sự khác biệt nằm ở dòng # 8)
xPheRe

1
@xPheRe À tôi hiểu rồi, cảm ơn. Tôi đã chỉnh sửa câu trả lời để phản ánh sự thay đổi đó
Garet Claborn

1
@xPheRe, tôi đoán tôi đã tập trung hơn vào việc chứng minh một điểm khi tôi thêm giải pháp này. Cảm ơn.
Lior Elrom

1
Câu trả lời hay +1, bạn có thể xem ECMAScript 6. Lớp từ khóa và phần mở rộng có sẵn: developer.mozilla.org/en-US/docs/Web/JavaScript/
Kẻ

26

Trong ES6 , bạn có thể sử dụng toán tử trải rộng như

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

Lưu ý rằng Object.assign () kích hoạt setters trong khi cú pháp lây lan không.

Để biết thêm thông tin, xem liên kết, Cú pháp phổ biến MDN


Câu trả lời cũ:

Trong ES6 , có Object.assignđể sao chép các giá trị thuộc tính. Sử dụng {}làm thông số đầu tiên nếu bạn không muốn sửa đổi đối tượng đích (thông số đầu tiên được thông qua).

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

Để biết thêm chi tiết, xem liên kết, MDN - Object.assign ()

Trong trường hợp nếu bạn cần là Polyfill cho ES5 , liên kết cũng cung cấp nó. :)


18

Và một năm sau, tôi có thể nói với bạn rằng có một câu trả lời hay khác.

Nếu bạn không thích cách tạo mẫu hoạt động để mở rộng trên các đối tượng / lớp, hãy xem xét tại đây: https://github.com/haroldiedema/joii

Mã ví dụ nhanh về các khả năng (và nhiều hơn nữa):

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"

Chà, tôi vẫn còn 2 tháng nữa cho đến khi hết 2 năm: P Dù sao đi nữa, JOII 3.0 sắp phát hành :)
Harold

1
Làm điều đó 3 năm sau.

Khái niệm thú vị, nhưng cú pháp trông thật xấu xí. Bạn nên chờ đợi các lớp ES6 trở nên ổn định hơn
ngủ

Tôi hoàn toàn đồng ý @s ngủycal. Nhưng thật không may, sẽ còn ít nhất 5 năm nữa trước khi tất cả các trình duyệt chính / phổ biến thực hiện điều này. Vì vậy, cho đến thời điểm đó, điều này sẽ phải làm ...
Harold

12

Những người vẫn đang đấu tranh cho cách tiếp cận đơn giản và tốt nhất, bạn có thể sử dụng Spread Syntaxđể mở rộng đối tượng.

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

Lưu ý: Hãy nhớ rằng, tài sản ở xa nhất bên phải sẽ có quyền ưu tiên. Trong ví dụ này, person2nằm ở bên phải, vì vậy newObjsẽ có tên Robo trong đó.



6

Đối tượng 'thông báo' của Mozilla kéo dài từ ECMAScript 6.0:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Class/extends

LƯU Ý: Đây là một công nghệ thử nghiệm, một phần của đề xuất 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;     } 
}

Công nghệ này có sẵn trong Gecko (Google Chrome / Firefox) - 03/2015 bản dựng hàng đêm.


4

Trong phần lớn dự án có một số triển khai mở rộng đối tượng: gạch dưới, jquery, lodash: extend .

Ngoài ra còn có triển khai javascript thuần túy, đó là một phần của ECMAscript 6: Object.assign : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


Không phải việc triển khai javascript thuần túy có phải là đề cập đến một cái gì đó được triển khai chỉ bằng JavaScript chứ không phải là một chức năng do môi trường cung cấp có thể được thực hiện nguyên bản không?
binki

1
@binki, ý tôi là việc triển khai javascript gốc - một phần của tiêu chuẩn ECMAScript 2015 (ES6)
Cezary Daniel Nowak

2
Function.prototype.extends=function(ParentClass) {
    this.prototype = new ParentClass();
    this.prototype.constructor = this;
}

Sau đó:

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

Cập nhật ngày 01/2017:

Xin vui lòng, bỏ qua câu trả lời của tôi năm 2015 vì Javascript hiện hỗ trợ extendstừ khóa kể từ 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

1
Bằng cách gọi new ParentClass()trước khi ghi đè hàm tạo, bạn đã thực hiện hàm tạo cha. Tôi không nghĩ đó là hành vi đúng nếu bạn hỏi tôi ...
Harold

1

Tóm lược:

Javascript sử dụng một cơ chế được gọi là kế thừa nguyên mẫu . Kế thừa nguyên mẫu được sử dụng khi tra cứu một tài sản trên một đối tượng. Khi chúng tôi mở rộng các thuộc tính trong javascript, chúng tôi đang kế thừa các thuộc tính này từ một đối tượng thực tế. Nó hoạt động theo cách sau:

  1. Khi một thuộc tính đối tượng được yêu cầu, (ví dụ myObj.foohoặc myObj['foo']) công cụ JS trước tiên sẽ tìm thuộc tính đó trên chính đối tượng đó
  2. Khi thuộc tính này không được tìm thấy trên chính đối tượng, nó sẽ leo lên chuỗi nguyên mẫu nhìn vào đối tượng nguyên mẫu. Nếu tài sản này cũng không được tìm thấy ở đây, nó sẽ tiếp tục leo lên chuỗi nguyên mẫu cho đến khi tài sản được tìm thấy. Nếu tài sản không được tìm thấy, nó sẽ đưa ra một lỗi tham chiếu.

Khi chúng tôi muốn mở rộng từ một đối tượng trong javascript, chúng tôi chỉ cần liên kết đối tượng này trong chuỗi nguyên mẫu. Có rất nhiều cách để đạt được điều này, tôi sẽ mô tả 2 phương pháp thường được sử dụng.

Ví dụ:

1. Object.create()

Object.create()là một hàm lấy một đối tượng làm đối số và tạo một đối tượng mới. Đối tượng được thông qua làm đối số sẽ là nguyên mẫu của đối tượng mới được tạo. Ví dụ:

// 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. Hoàn toàn thiết lập thuộc tính nguyên mẫu

Khi tạo các đối tượng bằng các hàm tạo, chúng ta có thể đặt các thuộc tính vào thuộc tính đối tượng nguyên mẫu của nó. Các đối tượng được tạo tạo thành hàm xây dựng khi sử dụng newtừ khóa, có nguyên mẫu được đặt thành nguyên mẫu của hàm xây dựng. Ví dụ:

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


0

Bạn chỉ có thể làm điều đó bằng cách sử dụng:

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

cập nhật: Tôi đã kiểm tra this[i] != nullnulllà một đối tượng

Sau đó sử dụng nó như:

var options = {
      foo: 'bar',
      baz: 'dar'
    }

    var defaults = {
      foo: false,
      baz: 'car',
      nat: 0
    }

defaults.extend(options);

Điều này cũng dẫn đến:

// defaults will now be
{
  foo: 'bar',
  baz: 'dar',
  nat: 0
}

0

XIN VUI LÒNG THÊM LÝ DO ĐỂ TẢI XUỐNG

  • Không cần sử dụng bất kỳ thư viện bên ngoài để mở rộng

  • Trong JavaScript, mọi thứ đều là một đối tượng (ngoại trừ ba kiểu dữ liệu nguyên thủy và thậm chí chúng được tự động bao bọc với các đối tượng khi cần thiết). Hơn nữa, tất cả các đối tượng là đột biến.

Lớp người trong 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);

Sửa đổi một thể hiện / đối tượng cụ thể .

alice.displayGreeting = function() 
{
    alert(this.getGreeting());
}

Sửa đổi lớp học

Person.prototype.getGreeting = function() 
{
    return 'Hi ' + this.getName() + '!';
};

Hoặc nói một cách đơn giản: mở rộng JSON và cả hai đối tượng đều giống nhau

var k = {
    name : 'jack',
    age : 30
}

k.gender = 'male'; /*object or json k got extended with new property gender*/

nhờ tác hại của ross, bụi diaz


-1

Điều này sẽ làm cho việc mở rộng các thuộc tính của bạn tạo ra một Object mới với các nguyên mẫu tham số đối tượng mà không làm thay đổi đối tượng được truyền.

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

Nhưng nếu bạn muốn mở rộng Object của mình mà không sửa đổi các tham số của nó, bạn có thể thêm extendProperty vào đối tượng của mình.

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

-2

Tạo mẫu là một cách hay, nhưng đôi khi nguyên mẫu khá nguy hiểm và có thể dẫn đến lỗi. Tôi thích gói gọn điều này vào một đối tượng cơ sở, giống như Ember.js đối với Ember.Object.extend và Ember.Object.reopen. Đó là an toàn hơn nhiều để sử dụng.

Tôi đã tạo ra một ý chính với cách bạn sẽ thiết lập một cái gì đó tương tự như những gì Ember.Object sử dụng.

Đây là liên kết: https://gist.github.com/WebCloud/cbfe2d848c80d4b9e9bd


9
Prototyping is a nice way, but prototype is quite dangerous sometimes and can lead to bugs.Ý bạn là như thế nào? Sử dụng chuỗi nguyên mẫu trong JavaScript có thể dẫn đến lỗi? Nó giống như nói rằng việc sử dụng các lớp trên Java có thể dẫn đến lỗi và hoàn toàn vô nghĩa.
HMR

@HMR anh ấy nói rằng việc mở rộng các nguyên mẫu đối tượng do môi trường cung cấp dẫn đến mã dễ vỡ có thể xung đột với tính năng ngôn ngữ JavaScript cốt lõi trong tương lai. Nếu bạn thêm một hàm tiện ích hữu ích cho mọi thứ bằng cách mở rộng Objectnguyên mẫu, thì hàm của bạn có thể có cùng tên với hàm JavaScript trong tương lai và khiến mã của bạn phát nổ khi chạy trong tương lai. Ví dụ: giả sử bạn đã thêm một repeat()hàm vào Objectvà gọi nó trong các Stringtrường hợp và sau đó thời gian chạy JavaScript của bạn được cập nhật lên ES6?
binki

@binki Cảm ơn bạn đã đóng góp. Bạn đang nói về việc thay đổi nguyên mẫu của các lớp mà bạn không "sở hữu" và do đó phá vỡ tham chiếu đóng gói: developer.mozilla.org/en/docs/Web/JavaScript/iêu JS không có các biến riêng tư để API của bạn hiển thị các thành viên triển khai , điều đó thường được giải quyết bằng quy ước (tên thành viên bắt đầu bằng dấu gạch dưới). Không chắc đó là vấn đề chính mà op có hay cú pháp gây nhầm lẫn và nhiều người không hiểu nó.
HMR

@HMR, tôi có thể sai, nhưng tôi nghĩ “nhưng nguyên mẫu là khá nguy hiểm” đề cập đến khuôn khổ nguyên mẫu nổi tiếngcó thể lợi dụng các prototypetính năng ngôn ngữ .
binki

Tạo mẫu rất nguy hiểm vì nếu bạn đang sử dụng các đối tượng bạn không tạo, bạn sẽ không biết tác dụng phụ sẽ là gì khi sử dụng chúng làm nguyên mẫu. Nhìn vào fiddle này ví dụ: jsfiddle.net/fo6r20rg
Arkain
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.