这篇文章是关于困惑我很久的JavaScript原型。在这篇文章中,我会从下面几个大纲解释JavaScript原型:
- 什么是JavaScript原型
- 是如何工作的
- _proto_和prototype
- 例子
- 使用prototype和不使用prototype的区别
- 性能问题
什么是JavaScript prototype
不像许多其他面向对象语言,JavaScript使用prototype来跟其他对象分享功能--这是什么意思?
Prototype 是JavaScript对象继承从另外一个对象继承特性的机制。它们分享根prototype对象中定义的数据和方法。
更简单的就是所有JavaScript对象从原型对象继承性能和方法。
如何工作的
我认为在Javascript的任何东西都是对象。函数是对象。字符串,布尔值,数字都是对象(他们是包裹在对象里的提供函数例如stirng.length的基元。数组也是对象。只有undefined不是对象。
耐心读取下面的文章,你会同意我的观点
为了提供继承,原始对象可以有一个prototype object这个像一个临时对象,原始对象可以从这个临时对象继承方法和属性。
字符串的父亲是String.prototype,数字的父亲是Number.prototype,数组的父亲是Array.prototype。
一个对象的prototype也可以有一个prototype对象,对象的prototype也可以从这个另一个prototype对象继承方法和属性。我们把这个称之为prototype chain(原型链)。这就是为什么有时候你会注意到不同的对象有在项目中可用的其他对象上定义的属性和方法。
例如,我们使用字符串"abc@gmail.com",你会使用一些函数例如toUpperCase,trim或split因为他存在在String.prototype上。
_proto_和prototype
_proto_是查找链中用于解析方法的实际对象。
prototype是使用new来创建一个对象的时候, 用于构建_proto_的对象。
更具体的说,属性和方法是定义在对象的构建函数prototype的属性上定义的,而不是在对象实例本身。
function FunPrototype() {
this.value = 0;
}
FunPrototype.prototype.increment = function () {
this.value++;
console.log("printed: ", this.value);
}
const instance = new FuncPrototype();
insatnce.increment();
一旦你声明上面的函数, FuncPrototype.prototype被创建在内部。函数increment被加到FuncPrototype.prototype,它是被使用new FuncPrototype()创建的FuncPrototype实例instance分享。
任何使用new FuncPrototype()创建的实例,都有一个``proto属性,这个属性指向FuncPrototype.prototype`。这是链,这个是被用来遍历来找到一个特殊对象的属性。
例如:
( new FuncPrototype )._proto_===
FuncPrototype.prototype; // true
( new FuncPrototype ).prototype === undefined; // true
注意:_proto_不是一个访问原型链的标准方式,标准但类似的方法是使用
Object.getPrototypeOf(object_name)。
例子
增加一个新的属性:
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyeColor;
}
// add new propertry
Person.prototype.nationality = "English";
// add new method
Person.prototype.name = function() {
return this.firstName + " " + this.lastName;
}
console.log打印出你可以看到name()和nationality。
使用prototype和不使用prototype之间的区别
当你不使用prototype,每次你创建一个新的实例,都会将属性和方法分配给该实例。这意味着他们会指向不同的内存引用。
使用原型可以更快地创建对象,因为每次创建新对象时都不必重新创建该函数。
性能问题
在这个部分,我会比较应用prototype和不应用prototype创建1000个实例的性能。然后调用函数increment()来看看花了多长时间。
代码如下:
function FuncNoPrototype() {
this.value = 0;
this.increment = function() {
this.value++;
console.log("printed: ", this.value);
}
}
const t0 = (new Date()).getTime();
for (let i = 0; i < 1000; i++) {
const instanceNoPrototype = new FuncNoPrototype();
instanceNoPrototype.increment();
}
const t1 = (new Date()).getTime();
function FuncPrototype() {
this.value = 0;
}
FuncPrototype.prototype.increment = function() {
this.value++;
console.log("printed: ", this.value);
}
const t2 = (new Date()).getTime();
for (let i = 0; i < 1000; i++) {
const instancePrototype = new FuncPrototype();
instancePrototype.increment();
}
const t3 = (new Date()).getTime();
document.getElementById("app").innerHTML = `
<h1>time to create 1000 instances</h1>
<div>without prototype: ${t1 - t0}</div>
<div>with prototype: ${t3 - t2}</div>
`;
总结
JavaScript 原型在前端项目中对于通过JavaScript建立的库和建立工具 仍然很重要。它是JavaScript语言的基础。我们必须很清晰的理解原型,以便在我们使用原型来提高代码的性能时做出决策。
此文章来源于Medium