11

我不明白在 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);
4

7 回答 7

8

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
于 2012-05-03T12:21:45.817 回答
8

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

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

现在,如果您向 中添加属性obj1,则该属性仅存在于 上obj1。同样obj2

如果将属性添加到Klass,则该属性同样仅存在于 Klass(函数对象)上。它完全没有obj1影响obj2

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

您可以在函数主体内添加代码Klass以将属性添加到this; 这将导致任何未来的Klass对象获得这些属性。但是每个对象都有自己的副本——它可以在内存方面累加起来,尤其是当属性是方法时——并且这些副本不会受到未来对Klass.

于 2012-05-03T13:22:23.223 回答
2

ES5Object.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();
于 2012-05-03T11:59:48.850 回答
1

prototype对象有点难以理解;然而这篇关于 OOP JavaScript 的文章可以帮助阐明一些观点。

简而言之,该prototype对象为“接收者”对象提供了蓝图——您所要做的就是将接收者的prototype属性指向您的蓝图对象。请注意,您可以拥有任意数量的原型蓝图对象接收者(因此 Car 和 Train 都可以指向一个公共 Vehicle 原型对象)。

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

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

您现在可以创建一个Car使用vechiclePrototype

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

使用原型继承的一大优点是两者的所有实例都Car共享Train完全相同的move函数(而不是创建同一函数的多个实例),如果这些对象有很多实例,这会导致显着的内存节省。

于 2012-05-03T12:19:35.987 回答
1

忽略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")

使用任何你想要的任意数量扩展函数_.extendjQuery.extend, pd.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
}
于 2012-05-03T12:57:45.870 回答
0

使用原型,您可以以“更清洁”的方式进行扩展,因为您将构造函数内部的逻辑与定义对象的属性和方法分开。

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

然而,这种扩展对象的方式与 Underscore.js 等现代 JavaScript 库无关,或者也可以在 jQuery 的帮助下编写得更简洁。

于 2012-05-03T11:55:06.557 回答
0

您可以使用该词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
于 2021-12-15T13:01:16.077 回答