一、JS内置对象

所谓的内置对象 指的是:JavaScript本身就自己有的对象 可以直接拿来就用。例如ArrayString等等。JavaScript一共有12内置对象

函数类型(10个)

- String

- Number

- Boolean

- Array

- Function

- Date

- RegExp

- Error

- Object

- Event

函数类型 有__proto__和prototype属性

对象类型(2个)

- Math

- JSON

对象类型只有__proto__属性

JavaScript原型链回顾

二、JS原型链

2.1 概述

JavaScript原型链回顾

- 每个函数都有prototype属性,除了Function.prototype.bind(),该属性指向原型。

- 每个对象都有__proto__属性,指向了创建该对象的构造函数的原型。其实这个属性指向了[[prototype]],但是[[prototype]]是内部属性,我们并不能访问到,所以使用__proto__来访问

- 对象可以通过proto来寻找不属于该对象的属性,__proto__将对象连接起来组成了原型链

打开浏览器的控制面板,随便输入一个JS内置的构造器函数,比如Array,控制台输出的是一个名为Array的函数体,这好像并没有什么稀奇的,但是,当你接着输入Array.prototype,控制面板输出了一堆我们经常用到的Array构造器的方法,把目光转移到最下方,有一个叫__proto__的属性,好奇的点开。列表列出的不是Object构造器的方法么,里边有我们非常熟悉的hasOwnProperty还有toString等方法。如果Array是构造器,那么控制面板输出的Array.prototype的所有属性中constructor又是什么构造器?点开看看,之后就像身处德罗斯特效应中一样,__proto__和constructor,还有Array构造器中常用的方法名不断的出现,一层套一层,一层层展开,没有尽头

JavaScript原型链回顾

拿Array举例,Array.prototype中有一个constructor属性,这个属性的值就是Array构造器自己

Array.prototype.constructor === Array //true

2.2 prototype

这是一个显式原型属性,只有函数才拥有该属性。基本上所有函数都有这个属性,但是也有一个例外

let fun = Function.prototype.bind()

如果你以上述方法创建一个函数,那么可以发现这个函数是不具有prototype属性的

2.2.1 prototype 如何产生的

当我们声明一个函数时,这个属性就被自动创建了

function Foo() {}

并且这个属性的值是一个对象(也就是原型),只有一个属性constructor

- constructor对应着构造函数,也就是Foo

2.2.2 constructor

constructor是一个公有且不可枚举的属性。一旦我们改变了函数的prototype,那么新对象就没有这个属性了(当然可以通过原型链取到constructor)

那么你肯定也有一个疑问,这个属性到底有什么用呢?其实这个属性可以说是一个历史遗留问题,在大部分情况下是没用的,在我的理解里,我认为他有两个作用:

- 让实例对象知道是什么函数构造了它

- 如果想给某些类库中的构造函数增加一些自定义的方法,就可以通过xx.constructor.method来扩展

2.3__proto__

这是每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型。其实这个属性指向了[[prototype]],但是[[prototype]]是内部属性,我们并不能访问到,所以使用__proto__来访问

- 因为在JS中是没有类的概念的,为了实现类似继承的方式,通过__proto__将对象和原型联系起来组成原型链,得以让对象可以访问到不属于自己的属性

2.3.1 实例对象的_proto_如何产生的

当我们使用new操作符时,生成的实例对象拥有了__proto__属性

function Foo() {}

// 这个函数是 Function 的实例对象

// function 就是一个语法糖

// 内部调用了 new Function(...)

所以可以说,在new的过程中,新对象被添加了__proto__并且链接到构造函数的原型上

2.3.2 new 的过程

- 新生成了一个对象

- 链接到原型

- 绑定this

- 返回新对象

在调用new的过程中会发生以上四件事情,我们也可以试着来自己实现一个new

function create() {

// 创建一个空的对象

let obj = new Object()

// 获得构造函数

let Con = [].shift.call(arguments)

// 链接到原型

obj.__proto__ = Con.prototype

// 绑定 this,执行构造函数

let result = Con.apply(obj, arguments)

// 确保 new 出来的是个对象

return typeof result === 'object' ? result : obj

}

- 对于实例对象来说,都是通过new产生的,无论是function Foo()还是let a = { b : 1 }

- 对于创建一个对象来说,更推荐使用字面量的方式创建对象。因为你使用new Object()的方式创建对象需要通过作用域链一层层找到Object,但是你使用字面量的方式就没这个问题。

// function 就是个语法糖

// 内部等同于 new Function()

let a = { b: 1 }

// 这个字面量内部也是使用了 new Object()

JavaScript原型链回顾

这里Array内置对象 且是函数类型。所以Array有__proto__属性 指向的是函数类型(function(){})。所以当我们在输出Array.__proto__.proto__;就会返回对象类型(Object{}).但是再向上就null。因为Object就是父类了。所有的继承自Object。

- JS内置构造器其中之一的Array原本就是一个函数,而这个函数就是Function的prototype,所以Function.prototype有的方法,JS内置构造器都有,比如call()、apply()、bind()等(其实我们自定义的函数也是继承自Function.prototype,所以我们自己也可以定义构造器)。而Function.prototype的进化链指针又指向了Object.prototype

// 数组实例的__proto__指向构造器的原型

[].__proto__ === Array.prototype

2.4 总结

- Object是所有对象的爸爸,所有对象都可以通过__proto__找到它

- Function是所有函数的爸爸,所有函数都可以通过__proto__找到它

- Function.prototype和Object.prototype是两个特殊的对象,他们由引擎来创建

- 除了以上两个特殊对象,其他对象都是通过构造器new出来的

- 函数的prototype是一个对象,也就是原型

- 对象的__proto__指向原型,__proto__将对象和原型连接起来组成了原型链

关于原型有3个相关的概念:

- 函数对象的prototype属性, 可以称之为显式原型属性(简称: 显式原型)

- 实例对象的__proto__属性, 可以称之为隐式原型属性(简称: 隐式原型)

- 原型对象: 也就是prototype属性和_proto__属性指向的对象

JavaScript原型链回顾

图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线

三、JSON和Math

JS内置的构造器函数都可以使用new关键字实例化一个对象,我们称实例化后的这个对象就是某某构造器的一个实例

JavaScript原型链回顾

我们试试JSON和Math能不能实例化对象​

JavaScript原型链回顾

JSON和Math不是构造器函数,他们是普通的对象。只有构造器函数才能使用new关键字实例化一个对象,而JSON和Math已经是对象了,所以我们可以不用实例化直接使用JSON和Math中的属性和方法

- 所以JSON和Math不属于10个构造器函数,但他们12个共同属于Javascript的内置对象

相关文章: