新葡亰496net Web前端 Js世襲背后的场景-prototype,__proto__, [[prototype]]

Js世襲背后的场景-prototype,__proto__, [[prototype]]



图片 1

在其他语言中,new操作符都是用来实例化创建一个对象的,JavaScript
中同样如此,但是它又有一些不同。为了说清楚这个问题我们先来看一下JavaScript
中的类、原型、原型链、继承这些概念吧。

如果你看的过程中觉得理解有些困难,把例子在代码中跑一跑,亲手试一试也许能解决不少疑惑。

prototype是构造函数的一个属性,它决定了在构造出来的对象上__proto__属性将是什么样的。

 

一切皆为对象

如上图所示,理解JavaScript中的继承的关键是要理解母鸡如何产蛋的过程。

JavaScript 中没有传统类的概念,它的类就是一个方法,也就是说JavaScript
中是通过function来定义类的。比如我们可以这样子来定义一个类。

殊不知,JavaScript的世界中的对象,追根溯源来自于一个 null

[[prototype]]、__proto__、prototype三者之间的联系

function Person(name, age) { 

「一切皆为对象」,这句着实是一手好营销,易记,易上口,印象深刻。

每个对象都可以有另一个对象作为其原型。然后前一个对象继承了它的所有原型属性。对象通过内部属性[[Prototype]]指定其原型。由[[Prototype]]属性连接的对象链称为原型链。

     this.name = name; 

万物初生时,一个null对象,凭空而生,接着Object、Function学着null的模样塑造了自己,并且它们彼此之间喜结连理,提供了prototype和constructor,一个给子孙提供了基因,一个则制造万千子子孙孙。

为了理解基于原型的继承是怎么样的,我们先来看一个例子。

     this.age = age; 

在JavaScript中,null也是作为一个对象存在,基于它继承的子子孙孙,当属对象。

var proto = { describe: function () { return 'name: '+this.name; }};var obj = { [[Prototype]]: proto, name: 'obj'}; obj.describe[Function] obj.describe()'name: obj'

     this.sing = function() { alert(this.name) } 

乍一看,null像是上帝,而Object和Function犹如JavaScript世界中的亚当与夏娃。

__proto__是Object.prototype对象的访问者属性。它暴露了访问它的对象的内部原型

}

原型指针 __proto__

function Foo(name) { this.name = name this.whoAmI = function () { return this.name }}var b = new Foo('b')var a = new Foo('a')b.say = function () { console.log(`Hi from ${this.whoAmI()}`)}console.log(a.__proto__ === Foo.prototype); // trueconsole.log(a.__proto__ === b.__proto__); // true

 

在JavaScript中,每个对象都拥有一个原型对象,而指向该原型对象的内部指针则是__proto__,通过它可以从中继承原型对象的属性,原型是JavaScript中的基因链接,有了这个,才能知道这个对象的祖祖辈辈。从对象中的__proto__可以访问到他所继承的原型对象。

JavaScript引擎在对象b上添加了一个say方法,而不是在Foo原型对象(Foo.prototype)上。正如上图中看到的那样,a.__
proto__暴露了指向Foo.prototype对象的[[Prototype]]。同样,b.__
proto__也指向与a.__ proto__相同的对象。

 

var a = new Array();

通过构造函数来创建对象

那么,有类了就一定存在着继承,而js的继承跟传统的类继承模型不同,它是使用
prototype 原型模型。这经常被当作是 JavaScript
的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。
实现传统的类继承模型是很简单,但是实现
js中的原型继承则要困难的多。JavaScript
使用原型链的继承方式。我们来看下这个例子。

a.__proto__ === Array.prototype // true

除了通过指定模式创建对象之外,构造函数还可以执行另一个有用的操作 –
它会自动为新创建的对象设置原型对象。此原型对象存储在构造函数的原型对象属性中。

复制代码

上面代码中,创建了一个Array的实例a,该实例的原型指向了Array.prototype。

我们可以使用构造函数用b对象重写前面的例子。因此,对象aFoo.prototype的作用:

function Foo() {

Array.prototype本身也是一个对象,也有继承的原型:

创建拥有原型对象属性x及方法calculate()的一个Foo对象。

    this.value = 42;

a.__proto__.__proto__ === Object.prototype  // true

function Foo(y) { this.y = y;}Foo.prototype.x = 10;Foo.prototype.calculate = function (z) { return this.x + this.y + z;};

}

// 等同于 Array.prototype.__proto__ === Object.prototype

使用对象Foo创建一个b对象实例。

Foo.prototype = {

这就说了明了,Array本身也是继承自Object的,那么Object的原型指向的是谁呢?

var b = new Foo(20);b.calculate(30); // 60console.log( b.__proto__ === Foo.prototype, // true b.__proto__.calculate === Foo.prototype.calculate // true b.__proto__.calculate === b.calculate, // true Foo === b.constructor, // true Foo === Foo.prototype.constructor, // true);

    method: function() {}

a.__proto__.__proto__.__proto__ === null  // true

正如上面打印出来的信息,对象b从Foo()继承了方法calculate。“Foo.prototype”自动创建一个特殊属性“constructor”,它是对构造函数本身的引用。实例b可以通过代理来找到它,并用来检测其构造函数。

};

// 等同于 Object.prototype.__proto__ === null

JavaScript经典继承图

 

图片 2

这也是通过构造函数来创建对象,但是在这一系列的对象和实例之间我们的焦点是放在原型链上。原型对象其实也是普通的对象,也有属于它们自己的属性。如果原型具有对其原型的非空引用,依此类推,则称为原型链。

function Bar() {}

所以说,JavaScript中的对象,追根溯源都是来自一个null对象。佛曰:万物皆空,善哉善哉。

以下是JavaScript经典继承的图表。构造函数Foo只是虚构类的类名。
foo对象是Foo的一个实例。

 

除了使用.__proto__方式访问对象的原型,还可以通过Object.getPrototypeOf方法来获取对象的原型,以及通过Object.setPrototypeOf方法来重写对象的原型。

现在我们可以从图中看到为什么当我们从Dog类继承Animal 时,我们会这样做:

// 设置Bar的prototype属性为Foo的实例对象

值得注意的是,按照语言标准,__proto__属性只有浏览器才需要部署,其他环境可以没有这个属性,而且前后的两根下划线,表示它本质是一个内部属性,不应该对使用者暴露。因此,应该尽量少用这个属性,而是用Object.getPrototypeof和Object.setPrototypeOf,进行原型对象的读写操作。

function Dog() {} // the usual constructor functionDog.prototype = new Animal();Dog.prototype.constructor = Dog;

Bar.prototype = new Foo();

这里用__proto__属性来描述对象中的原型,是因为这样来得更加形象,且容易理解。

当通过new操作符创建一个实例的时候,都发生了些什么:

Bar.prototype.foo = ‘Hello World’;

图片 3

注意Foo.prototype的原型(prototype)并不是来自原型链。

 

原型对象 prototype

// 修正Bar.prototype.constructor为Bar本身

函数作为JavaScript中的一等公民,它既是函数又是对象,函数的原型指向的是Function.prototype

Bar.prototype.constructor = Bar;

var Foo = function() {}

 

Foo.__proto__ === Function.prototype // true

var bar= new Bar() // 创建Bar的一个新实例

函数实例除了拥有__proto__属性之外,还拥有prototype属性。

 

通过该函数构造的新的实例对象,其原型指针__proto__会指向该函数的prototype属性。

// 原型链

var a = new Foo();

标签:

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图