什么是js的原型以及原型链

1.为什么要有原型和原型链

JavaScript中其实并没有类的概念。从es6通过class关键字才引入了Class这个概念,但是ES6的class可以看作是一个语法糖,它的绝大部分功能,ES5都可以做到。。

es6之前,实现生成实例对象的传统方法是通过构造函数。将实例对象与构造函数连接起来的就是原型原型链

2.什么是原型?

JavaScript中有两个原型:

  • __proto__:隐式原型
  • prototype:显式原型

但是,这两种原型并不存在于JavaScript的所有对象中。那么在哪里可以体现出这两个原型呢?先看代码:

	var a = []
	var o = {
    
    }
	function fn(){
    
    }

	console.log(a.__proto__); // 非undefined
	console.log(a.prototype); // undefined

	console.log(o.__proto__); // 非undefined
	console.log(o.prototype); // undefined
	
	console.log(fn.__proto__); // 非undefined
	console.log(fn.prototype); // {constructor: f}

结果如下:

image

可以看到:

  • 数组、对象、函数三个引用类型都有__proto__属性(隐式原型)

  • 只有函数除了__proto__之外还有一个prototype属性(显式原型)

所有的函数默认都会拥有一个名为prototype的公有且不可枚举(enumerable)的属性,它会指向另一个空的对象。

下面用构造函数来说明:

image

可以看到,a是通过构造函数创建的一个新对象,通过a.__proto__可以看到foo.prototype的内容。其实a.__proto__就是foo.prototype的引用,它俩指向的都是同一个地址(a.__proto__ === foo.prototype // true)。

小结:

  1. 实例对象a只有隐式原型(__proto__);构造函数既有隐式原型(__proto__)也有显式原型(prototype)。

  2. __proto__prototype都是对象,是对象说明它们也都有.__proto__属性:

    a.__proto__.__proto__
    foo.prototype.__proto__
    
  3. 实例对象的隐式原型指向了构造函数的显式原型:a.__proto__ === foo.prototype

  4. 访问实例对象的某个属性,如果实例对象上没有,就通过实例对象.__proto__找到构造函数的prototype继续找。

    function foo() {
          
           }
    
    foo.prototype.name = "张三"
    
    var a = new foo()
    
    a.name // 张三
    

3. 原型链

什么是原型弄懂了,原型链是什么大致也就明了了。先看如下代码的输出:

	function foo() {
    
     }

	foo.prototype.name = "张三"

	var a = new foo()

	console.log('1------', a.__proto__);
	console.log('2------', a.__proto__.__proto__);
	console.log('3------', a.__proto__.__proto__.__proto__);

	console.log(a.name); // 张三
	console.log(a.toString()); // [object Object]

结果如下:

可以看到:

  • a.__proto__:实例对象a的__proto__,指向的是构造函数foo的prototype

  • a.__proto__.__proto__:实例对象a的__proto____proto__指向的是Object()构造函数对象。

  • a.__proto__.__proto__.__proto__Object对象的__proto__最终指向了null。

这样一级一级的__proto__相连接起来的链式结构就是**原型链*。*

而在最后两行的console中:

  • a.name输出的是"张三",但是实例对象a并没有name这个属性,它是js引擎通过原型链查找到a.__proto__(即foo.prototype)找到的。

  • a.toString()这个方法在foo构造函数以及a实例对象中均没有,它存在于原型链中的a.__proto__.__proto__找到了Object()构造函数对象的prototype(显式原型)上。

因此,在查找一个对象的属性时,JS引擎就是在这样的原型链上一级一级的向上查找的,如果找到顶层的Object对象的__proto__还是没有找到某个属性,就会抛出错误。

4.关于constructor

可以看到,不管是实例对象a还是构造函数foo,又或者是Object()构造函数。它们的__proto__(隐式原型)上都有一个constructor属性。

这个constructor属性是所有函数的prototype属性自带的一个属性,它不可枚举(enumerable = false)且通过new foo()创建的对象也有一个constructor属性,指向了这个对象的构造函数。所以,实例对象、构造函数的原型、构造函数三者的关系是这样的:

a.constructor === foo.prototype === foo

其实,构造函数的原型上的constructor就是当前这个构造函数的引用:

	function foo() {
    
     }

	console.log(foo.prototype.constructor === foo); // true

猜你喜欢

转载自blog.csdn.net/vet_then_what/article/details/125515325