深入javascript计划五:深入浅出原型

看例子:

函数:

function test() {}
console.dir(test)

对象:

let test = {}
console.dir(test)

 从上面例子可以看得出,只有函数才有原型(prototype)属性,它们都有__proto__属性(下文会讲解)。

prototype

只有函数才会产生prototype,但是也有一个例外:

let fun = Function.prototype.bind()

这个创建函数是没有prototype属性的(下文会讲解)。

prototype是如何产生的?

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

function Person() {}

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

constructor对应着构造函数(也就是Person函数)。

constructor

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

function Person() {}
console.log(Person.prototype)
Person.prototype = { a: 1}
console.log(Person.prototype)

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

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

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

__proto__

这是每个对象都有的隐式原型属性,指向了创建该对象的构造函数的原型。

其实这个属性指向了 [[prototype]],但是 [[prototype]] 是内部属性,我们并不能访问到,所以使用__proto__来访问。

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

实例对象__proto__是如何产生的?

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

function Person() {}
// 这个函数是 Function 的实例对象
// function 就是一个语法糖
// 内部调用了 new Function(...)

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

new过程:

  1. 新生成一个新对象
  2. 链接到原型
  3. 绑定this
  4. 返回新对象

自己实现一个 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 Person() {}
// function是语法糖
// 内部等同于 new Function()

还是(字面量)

let a = { b : 1 }
// 内部使用了new Object()

对于创建一个对象来说,更推荐使用字面量的方式创建对象。

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

Function.__proto__ === Function.prototype

大图:

对于对象来说,xx.__proto__.contrcutor 是该对象的构造函数,但是在大图中我们可以发现 Function.__proto__ === Function.prototype,难道这代表着 Function 自己产生了自己?

答案:肯定是否认的,要说明这个问题我们先从 Object 说起。

Object

console.dir(Object)

展开prototype

 

从大图中我们可以发现,所有对象都可以通过原型链最终找到 Object.prototype,

虽然 Object.prototype 也是一个对象,但是这个对象却不是 Object 创造的,而是引擎自己创建了 Object.prototype

所以可以这样说,所有实例都是对象,但是对象不一定都是实例。

Function

console.dir(Function)

 

 我们知道函数都是通过 new Function() 生成的,难道 Function.prototype 也是通过 new Function() 产生的吗?

答案:也是否定的,这个函数也是引擎自己创建的。

首先引擎创建了 Object.prototype ,然后创建了 Function.prototype ,并且通过 __proto__ 将两者联系了起来。

 

这里也很好的解释了上面的一个问题(为什么 let fun = Function.prototype.bind() 没有 prototype 属性)。

因为 Function.prototype 是引擎创建出来的对象,引擎认为不需要给这个对象添加 prototype 属性。

 

所以我们又可以得出一个结论,不是所有函数都是 new Function() 产生的。

有了 Function.prototype 以后才有了 function Function() ,然后其他的构造函数都是 function Function() 生成的。

 

现在可以来解释 Function.__proto__ === Function.prototype 这个问题了

因为先有的 Function.prototype 以后才有的 function Function() ,所以也就不存在鸡生蛋蛋生鸡的悖论问题了。

 

对于为什么 Function.__proto__ 会等于 Function.prototype ?

个人的理解是:其他所有的构造函数都可以通过原型链找到 Function.prototype ,并且 function Function() 本质也是一个函数,为了不产生混乱就将 function Function() 的 __proto__ 联系到了 Function.prototype 上。

总结

  1. Object 是所有对象的爸爸,所有对象都可以通过 __proto__ 找到它
  2. Function 是所有函数的爸爸,所有函数都可以通过 __proto__ 找到它
  3. Function.prototype 和 Object.prototype 是两个特殊的对象,他们由引擎来创建
  4. 除了以上两个特殊对象,其他对象都是通过构造器 new 出来的
  5. 函数的 prototype 是一个对象,也就是原型
  6. 对象的 __proto__ 指向原型, __proto__ 将对象和原型连接起来组成了原型链

 

还不懂的话看下面

 

var a = {};
console.log(a.prototype); //undefined
console.log(a.__proto__);  //Object {}

var b = function(){}
console.log(b.prototype); //b {}
console.log(b.__proto__);  //function() {}

/*1、字面量方式*/
var a = {};
console.log(a.constructor); //function Object() { [native code] } (即构造器Object)
console.log(a.__proto__ === a.constructor.prototype); //true

/*2、构造器方式*/
var A = function (){}; var a = new A();
console.log(a.constructor); // function(){}(即构造器function A)
console.log(a.__proto__ === a.constructor.prototype); //true

/*3、Object.create()方式*/
var a1 = {a:1} 
var a2 = Object.create(a1);
console.log(a2.constructor); //function Object() { [native code] } (即构造器Object)
console.log(a2.__proto__ === a1);// true 
console.log(a2.__proto__ === a2.constructor.prototype); //false(此处即为图1中的例外情况)

var A = function(){};
var a = new A();
console.log(a.__proto__); //Object {}(即构造器function A 的原型对象)
console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
console.log(a.__proto__.__proto__.__proto__); //null

抄袭:

https://juejin.im/post/5835853f570c35005e413b19

https://github.com/KieSun/Dream/issues/2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

An_s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值