何时使用“原型”一词向 javascript 中的对象添加新属性

When to use the word "prototype" in adding new properties to an object in javascript?

本文关键字:添加 对象 新属性 属性 javascript 原型 何时使      更新时间:2023-09-26

我不明白在JavaScript中何时使用"原型"一词,还是使用简单的"点"符号而不使用"原型"一词。有人可以查看这些代码块并帮助我了解何时要使用一个而不是另一个吗?

使用"原型":

function employee(name,jobtitle)
{
  this.name=name;
  this.jobtitle=jobtitle;
}
var fred=new employee("Fred Flintstone","Caveman");
employee.prototype.salary=null;
fred.salary=20000;
console.log(fred.salary);

没有"原型":

function employee(name,jobtitle,salary)
{
  this.name=name;
  this.jobtitle=jobtitle;
  this.salary=salary;
}
var fred=new employee("Fred Flintstone","Caveman", 20000);
console.log(fred.salary);

JavaScript 对象有一个属性,该属性是指向另一个对象的指针。此指针是对象的原型。默认情况下,对象实例共享相同的原型:

function Employee(name){
  this.name = name;
}
Employee.prototype.company = "IBM";
Employee.prototype.who = function(){
  console.log("My name is", this.name, "I work for", this.company);
}
var bob = new Employee('Bob');
var jim = new Employee('Jim');
// bob and jim are seperate objects, but each is linked to the same 'prototype' object.
jim.who(); // jim doesn't have a property called 'who', so it falls back to it's 'prototype', where who exists
// My name is Jim I work for IBM
bob.who();
// My name is Bob I work for IBM
// Bob leaves IBM for Microsoft
bob.company = "Microsoft"; // bob now has a property called 'company'. The value of which is 'Microsoft', which overrides bob's prototype property of the same name.
bob.who();
// My name is Bob I work for Microsoft
Employee.prototype.company = 'Facebook';
jim.who(); 
// My name is Jim I work for Facebook
bob.who(); // Bob is not affected by the change.
// My name is Bob I work for Microsoft
delete bob.company;
bob.who(); // bob no longer has it's own property 'company', so like jim, it drops down to the prototype object.
// My name is Bob I work for Facebook

围绕JS和继承的问题可能很复杂,但你的问题的答案相对简单。 请考虑以下代码:

 function Klass() { }
 var obj1 = new Klass();
 var obj2 = new Klass();

现在,如果将属性添加到 obj1 ,则该属性仅存在于 obj1 上。 同样obj2.

如果将属性添加到 Klass ,则该属性同样仅存在于 Klass(函数对象(上。 它根本不影响obj1obj2

但是如果你在Klass.prototype中添加一个属性,那么该属性将同时存在于obj1obj2上,以及通过new Klass创建的任何未来对象上。 如果随后更改原型上的属性值,则更改的值将是您在所有这些对象上看到的值。

你可以在Klass函数的主体中添加代码来向this添加属性;这将导致任何将来的Klass对象获得这些属性。 但是每个对象都有自己的副本 - 它可以在内存方面加起来,特别是当属性是方法时 - 并且这些副本不会受到未来对Klass正文的更改的影响。

ES5 的Object.create几乎不再需要麻烦.prototype

所以,拿@Gerry的例子,你可以像

var Mammal = {
    walk: function() {}
};
var Dog = Object.create(Mammal, {
    bark: {
        value: function() {}
    }
}); // create a new object which [[prototype]] refers to Mammal
Dog.walk();
Dog.bark();

prototype对象有点难以理解;但是这篇关于OOP JavaScript的文章可以帮助阐明一些问题。

简而言之,prototype对象为"收件人"对象提供了蓝图 - 您所要做的就是将收件人的prototype属性指向蓝图对象。 请注意,您可以根据需要拥有任意数量的原型蓝图对象的接收者(因此 Car 和 Train 都可以指向一个通用的车辆原型对象(。

您可以自由定义原型对象中的属性和函数,任何接收对象都可以使用,例如:

var vehiclePrototype = {
    // A property which will be supplied to the recipient
    cost: 0,
    // A method which will be supplied the recipient
    move: function () { 
        // Your prototype can refer to 'this' still.
        console.log("Moving " + this.name);
    };
}

您现在可以创建一个利用vechiclePrototypeCar

// Factory method for creating new car instances.
function createCar(name) {
    // Define the Car's constructor function
    function Car(name) {
        this.name = name;
    }
    // Point the car's prototype at the vechiclePrototype object
    Car.prototype = vechiclePrototype;
    // Return a new Car instance 
    return new Car(name);
}
// Create a car instance and make use of the Prototype's methods and properties
var mustang = createCar(mustang);
mustang.cost = 5000;
mustang.move();

可以以类似的方式创建新的 Train 对象:

function createTrain(whilstleSound) {
    // Define the Train's constructor function
    function Train(name) {
        this.whilstleSound = whilstleSound;
    }
    // Point the train's prototype at the vechiclePrototype object
    Train.prototype = vechiclePrototype;
    // Return a new Train instance 
    return new Train(name);
}
var ic125 = new Train("pooop pooop");
ic125.move();

使用原型继承的一大优点是CarTrain的所有实例共享完全相同的move函数(而不是创建同一函数的多个实例(,如果这些对象的许多实例,这将节省大量内存。

忽略new,忽略.prototype它们只是令人困惑的概念。如果你真的想要原型继承,请使用Object.create但大多数时候继承完全是矫枉过正。(原型继承应仅用作优化技术(。

构建类时,只需创建对象并扩展它们。

var Walker = {
    walk: function() {}
}
var Eater = {
    eat: function () {}
}
var Dog = extend({}, Eater, Walker, {
    bark: function () {},
    sniffBehind: function () {}
})
function dog(dogName) {
    return extend({}, Dog, {
        name: dogName
    })
}
var steveTheDog = dog("steve")
console.log(steveTheDog.name === "steve")

使用任意的arity扩展函数,_.extendjQuery.extendpd.extend等。

PD实现如下extend

function extend(target) {
    [].slice.call(arguments, 1).forEach(function(source) {
        Object.getOwnPropertyNames(source).forEach(function (name) {
            target[name] = source[name]
        })
    })
    return target
}

使用原型,您可以以"更干净"的方式进行扩展,因为您将构造函数中的逻辑与定义对象的属性和方法分开。

var Mammal = function() { ... };
Mammal.prototype = {
  walk: function() { ... }
};
var Dog = function() { ... };
for (var prop in Mammal.prototype) {
  Dog.prototype[prop] = Mammal.prototype[prop];
}
Dog.prototype.bark = function() { ... };

但是,上面没有原型的内容可能如下所示:

var Mammal = function() {
  this.walk = function() { ... };
};
var Dog = function() {
  Mammal.apply(this);
  this.bark = function() { ... };
};

然而,这种扩展对象的方式被现代JavaScript库(如Underscore.js(变得无关紧要,或者也可以在jQuery的帮助下编写得更干净。

您可以使用

单词prototype在整个应用程序中为特定类型(数组、函数、自定义类型的数量(定义一些功能

例如,您可以使用属性 sum 扩展所有数组:

const arrayPrototype = Array.prototype
Object.defineProperty(arrayPrototype, 'sum', { 
   get() { return this.reduce((a,b) => a + b, 0) }
})

执行代码块后,所有数组都具有以下属性:

console.log([1,3,-1,10].sum) // prints 13