《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 的属性访问构造函数。
如果不手动破坏原型链,那么通过构造函数创建新对象时,三者间的关系: