原型如何在引擎盖下工作?

时间:2017-07-08 23:29:35

标签: javascript performance prototype interpreter

  

“当您要创建批次时,将函数放在原型对象上   特定类型的对象的副本,他们都需要分享   共同的行为。通过这样做,你将通过刚刚保存一些内存   每个功能的一个副本,但这只是最简单的好处。“

这究竟是如何在幕后工作的?

2 个答案:

答案 0 :(得分:1)

在引入属性getter和setter之前,这并不像以前那么简单。

JavaScript对象(和null)

javascript对象是对象属性及其在内存中的值的某种集合。 javascript对象 value 是对这种数据集合的某种引用。 javascript null值用于表示对象的缺席。虽然" typeof"操作员返回"对象"当应用于null时,这是一个早期的语言设计缺陷,根深蒂固,无法改变:" null"是自己的数据类型,null不是对象。

用于存储对象数据的内存结构未在语言标准中定义。关于不同实现如何处理和存储对象数据的一些讨论是可用的,例如V8's object data storage.

上的这个答案

私有媒体资源和广告位

私有内部属性在各种类型的对象的语言标准中定义,但通常不能从JavaScript代码访问。内部属性通常被称为" slot"在语言文档中,并写在双方后面符号中。例如,对象的内部[[Callable]]槽对于函数对象设置为true,但只能通过查看typeof运算符是否返回" function"来检查代码。当应用于对象时。

[[原型]]插槽

一个这样的内部插槽是[[prototype]],它是对原型链中下一个对象的对象引用,或null如果已到达链的末端并且没有其他对象连锁,链条。可以使用Netscape风格浏览器中对象的非可枚举__proto__ getter / setter属性访问其值(Microsoft最初将JavaScript克隆到Jscript时未实现它)或使用ECMAScript中引入的Object.getPrototypeOf 5.1。 [[prototype]]属性是可变的,但在`Object.setPrototypeOf'文档中不鼓励在现有对象上更改它。

命名对象属性

命名对象属性可以实现为存储在对象中的键值对,也可以实现为使用Object.defineProperty(或多个属性的关联Object.defineProperties方法为该属性提供的getter / setter函数的组合)。

本地或"拥有"特性

属性值可以位于对象引用的数据集合中,也可以通过搜索为对象设置的[[prototype]]指针链("继承链")来访问。可以使用对象的.hasOwnProperty方法来检查属性是否是被引用对象的本地属性。

财产权限如何运作

  1. 对象属性的getter和setter优先于键值对的使用。如果使用setter / getter对定义了对象的属性,则将调用setter或getter函数来读取或写入属性,即使该属性是继承的也是如此。从表面上看,这表明当正在编写的对象属性尚未作为本地属性存在时,必须搜索原型链的setter。 如何对其进行优化将取决于JavaScript引擎,此处不予介绍。

  2. 对于普通的键值对象属性,读取属性将按对象和[[prototype]]链接的顺序搜索对象及其原型链,以查看该属性是否已存在。如果是,它的值从它被发现的地方返回。如果在到达原型链末尾之前未找到,则返回undefined作为属性值。

  3. 编写键值类型属性 而不使用setter或getter ,只需将值写为要写入的对象的本地属性,即可创建如果已写入的属性名称尚不存在,则为新属性。

    在本地编写键值属性意味着在读取它时,将返回本地值而不搜索继承链。因此,在一个实例对象上写入继承属性的值不会影响同一个类的另一个对象继承的值。

  4. delete运算符仅从对象中删除本地属性。如果删除了已写入的继承属性,则其值将恢复为继承的值。尝试删除正在继承的对象属性无效。

  5. 原型链来自何处。

    Javascipt中的对象由构造函数创建。每个构造函数(对象)都有一个名为prototype的属性。构造对象时,其内部[[prototype]]槽设置为其构造函数的prototype属性中保存的对象值,在创建对象时。如果你需要去那里,这意味着更改构造函数的prototype属性的值不会影响以前由构造函数创建的对象。

    类声明设置一个与类名同名的构造函数。在类声明中定义的方法被添加为构造函数的prototype属性的属性,其效果是它们由类实例对象继承。

    使用classfunction声明的构造函数之间的一个很大区别是类构造函数的prototype属性是只读的,不能更改(注意原型的属性不是只读的)并且可以改变)。这种机制保护类和扩展的原型链在声明类结构后不会被更新。

    另请参阅MDN上的Inheritance and the prototype chain或网页搜索"原型继承如何在javascript"中工作。

答案 1 :(得分:0)

密钥曾经是隐藏属性(现在是标准的)__proto__

var z = { weight: "Too much" };
var a = { name: "Bob", age: 32 };
var b = { name: "Doug" };

a.__proto__ = z;
b.__proto__ = a;

b.age; // 32 -- doesn't have one, so it checks __proto__.age
b.name; // Doug -- has its own, so it doesn't look it up
b.weight; // Too much -- __proto__.__proto__.weight

库中或标准方法/语法中的所有不同帮助程序实际上只是隐藏它的方法。

将其应用于ES构造函数时:

function Person (name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.weight = "That's rather personal";

var bob = new Person("Bob", 32);
bob.__proto__ === Person.prototype; // true

所以你可以在构造函数的开头想到三条不可见的行:

function Person (name, age) {
  /*
    this = {};
    this.constructor = Person;
    this.__proto__ = Person.prototype;
  */
  this.name = name;
  this.age = age;
  // return this;
}

ES6类实际上只是围绕这个构造函数行为的一个更漂亮的包装器,它隐藏了人们为使类继承工作所必须做的所有麻烦。