StepByStep前端科普系列(002)-prototype-based object-orientation

111 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情

省流阅读:对象的 __ proto__ 属性 指向其构造函数对象的prototype

技术背景:我们由维基百科对JS的定义说起,首先引用一下英文释义,

JavaScript is a high-level, often just-in-time compiled language that conforms to the ECMAScript standard. It has dynamic typing, prototype-based object-orientation, and first-class functions. It is multi-paradigm, supporting event-driven, functional, and imperative programming styles.

这里我们只关注两个释义,

1.first-class functions

头等函数也就是我们通常所说的“函数是头等公民”,这意味着,函数可以作为别的函数的参数,作为函数返回值,赋值给变量,或者,存储在数据结构中。这样,函数的名字就没有特殊含义,可当作具有函数类型的普通变量来对待,函数与其他数据类型一样,处于平等地位。

JS历史:借鉴Scheme语言,将函数提升到"第一等公民"(first class)的地位;

2.prototype-based object-orientation

翻译成中文:基于原型的面向对象设计。基于原型的编程是面向对象编程的一种方式,JS便是基于这种方式的一种主流开发语言。与之相对应的,基于类的主流开发语言有Java,C++。JS中没有Class的概念,虽然ES6引入类,但实质上这只是基于原型的继承的语法糖而已,并没有引入新的面向对象的继承模型。

基于类的语言,对象或实例(通常)通过类构造器来创建。基于原型的语言,对象通过复制已有对象或者通过扩展空对象创建。在JS中,复制并不是真正的复制一个对象,而是使新对象持有一个原型的引用。

JS历史:借鉴Self语言,使用基于原型(prototype)的继承机制。

JS有两种对象,Object 和 Function

Step1: Object,JS中所实例有对象都有1个__proto__属性,__proto__属性也称为隐式原型,这个属性可看作是对象链接原型对象的‘指针’。__proto__属性是对象所独有的

一个对象的隐式原型指向构造该对象的构造函数的原型。

var foo = {name:'foo',one:1,two:2}
var bar = {three:3}

bar.__proto__ = foo;

这里声明了两个对象,并将其中一个的原型属性指向foo,这样,bar的原型对象就变成foo,也意味着bar可以访问foo中的变量。

bar.one // 输出1

bar.name = 'newFoo' foo.name // 依旧输出 foo

bar对象持有其原型对象的副本,修改其原型的属性,不会导致原有原型对象被修改。

Step2:prototype属性是函数所独有的;prototype则是 Function 对象的属性 prototype也称显式原型,每一个函数在创建之后都会拥有一个名为prototype的属性,这个属性指向函数的原型对象。因为函数也是一种对象,所以同时拥有__proto__属性和prototype属性。

扩展:

  1. 方法这个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法。

2.原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。

// 构造函数
function Foo(y) {
  this.y = y;
}

// 修改 Foo 的 prototype,加入一个成员变量 x
Foo.prototype.x = 10;

// 修改 Foo 的 prototype,加入一个成员函数 calculate
Foo.prototype.calculate = function (z) {
  return this.x + this.y + z;
};

// 现在,我们用 Foo 这个原型来创建 b 和 c
var b = new Foo(20);
var c = new Foo(30);

// 调用原型中的方法,可以得到正确的值
b.calculate(30); // 60
c.calculate(40); // 80

图源来自英文文章 dmitrysoshnikov.com/ecmascript/…

Step3:__proto__隐式属性和prototype显式属性的作用

3.1 __proto__属性:当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象寻找,类似JAVA双亲委托机制,一直找,直到__proto__属性为null,再往上会报错,因为null没有原型。

原型链:通过__proto__属性将对象的继承关系连接起来,这条链路就叫原型链。

3.2 prototype属性:让该函数所实例化的对象们都可以找到公用的属性和方法。 普通对象.proto 指向它的构造函数的原型。即,f1.proto === Foo.prototype.

3.3 比较特殊的的是原型对象prototype,它本身也是一个对象,它的构造函数就是Object,所以 函数.prototype.proto === Object.prototype

一图胜千言

参考资料1

参考资料2

以上。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情