前端入门13-JavaScript进阶之原型

《JavaScript权威指南》

MDN web docs

Github:smyhvae/web

作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。

PS:梳理的内容以《JavaScript权威指南》这本书中的内容为主,因此接下去跟 JavaScript 语法相关的系列文章基本只介绍 ES5 标准规范的内容、ES6 等这系列梳理完再单独来讲讲。

正文-原型

JavaScript 中并没有 Java 里的类,但它有构造函数,也有继承,只是它是动态的基于原型的继承。所以,原型有点类似于 Java 中父类的概念。

但是,JavaScript 中的关于实例、继承等这些跟 Java 还是有很大的区别。

先来说说在 Java 里面:

类是静态的,类是可继承的,是对象的抽象模型的表现,每个具体的对象都是从类上实例化出来的,一个类中定义了这类对象的属性和行为,一旦定义完了运行时就无法改变了。

但对于 JavaScript 来说,它并没有类的存在,在 JavaScript 里,除了原始类型外,其余皆是对象。

它是动态的基于原型的继承机制,原型本质上也是对象,也就是说对象是继承自对象而来的。

而对象这个概念是实例化后的每一个具体个体代表,它是运行期动态生成的,再加上 JavaScript 里对象的特性,如可动态添加属性,这就让 JavaScript 里的继承机制非常强大,因为这样一来,它是可动态继承的,原型对象上发生的变化能够同步让继承它的子对象都跟随着变化。

原型概念

函数和构造函数的区别就在于,所有的函数,当和 new 关键字一起使用时,此时称它为构造函数。类似的关系,所有的对象,当它被设置为某个构造函数的 prototype 属性值时,此时称它为原型。

也就是说,任何对象都可以当做其他对象的原型。

在 Java 中,对象一般通过 super 关键字指向它的父类,而在 JavaScript 中,对象可通过 __proto__ 来指向它的原型对象,或者通过构造函数的 prototype 指向对象的原型。

prototype & __proto__

这两个虽然指向的是同一个原型对象,但它们的宿主却不一样,需要区分一下,prototype 是构造函数的属性,__proto__ 是通过构造函数创建出来的对象的属性。

__proto__ 属性并不在 ES5 标准规范中,但基本大部分浏览器都为引用类型实现了这么一个属性,用于查看当前对象所继承的原型,它的值等于该对象的构造函数的 prototype 属性值。

prototype 是每个函数对象的一个属性,其他对象并没有这个属性,因为基本所有的对象其实都是通过构造函数创建出来的,所以也只有函数才能来实现继承的机制。这个属性值表示着从这个构造函数创建的对象的原型是什么。

对象一节学习过,创建一个对象的三种方式:

//对象直接量: var a = {};//其实是 var a = new Object(); 的语法糖 var a = [];//其实是 var a = new Array(); 的语法糖 //构造函数 var a = new Array(); //Object.crate() var a = Object.crate(null);

所以,对象直接量的方式本质上也是通过构造函数的方式创建对象。

这也是为什么会在对象一节中说,所有通过直接量方式创建的对象都继承自 Object.prototype 的理由。

而通过 Object.create() 方式创建的对象,其原型就是参数指定的对象,可手动传入 null,表示创建的对象没有原型。

所以,在 JavaScript 中,绝大部分的对象都有原型,即使不手动指定,也会有默认的内置原型对象。之所以说绝大部分,是因为原型链顶部的 Object.prototype 对象的原型是 null,或者通过 Object.create() 创建对象时手动指定 null。

默认的继承结构

如果不手动指定继承关系,默认的几种引用类型的继承关系(原型链)如下:

声明的每个函数 -> Function.prototype –> Object.prototype -> null

数组对象 -> Array.prototype -> Object.prototype -> null

对象直接量创建的对象 -> Object.prototype -> null

自定义构造函数创建的对象 -> {} -> Object.prototype -> null

所有对象继承的顶层原型是 Object.prototype。

这也是为什么函数对象、数组对象、普通对象都可以使用一些内置的方法,因为创建这些对象的时候,默认就会有一些继承关系,跟 Java 中所有的类都继承自 Object 的机制类似。

构造函数和原型的关系

构造函数本身是一个函数对象,它的属性 prototype 指向的是另一个对象,所以这两个概念本身就是两个不同的东西。

通过一个构造函数创建一个新的对象,不能说,这个对象继承自构造函数,而是应该说,这对象继承自构造函数的属性 prototype 指向的对象。

所以,可以通俗的理解,构造函数只是作为第三方类似于工具的角色,用来创建一个新对象,然后让这个新对象继承自 prototype 属性指向的对象。

不过构造函数和原型之间是相互引用的关联关系,构造函数有个属性 prototype 指向原型,而原型也有一个属性 constructor 指向构造函数。

所以,所有从这个构造函数创建的新对象,都继承了原型的属性,那么这些新对象也就可以通过继承而来的 constructor 的属性访问构造函数。

如果不手动破坏原型链,那么通过构造函数创建新对象时,三者间的关系:

三者关系

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxfpy.html