__proto__ 和 prototype 以及 constructor

很多小伙伴其实对prototype这个东西都是一知半解的,更别说__proto__,今天就让我们来梳理一下他们的关系,首先我们通过一段代码来看下这几个属性的概念

function User(name){
	this.name=name
}
User.prototype.getName = function(){
	return this.name
}
const user = new User('henry')
console.log(user.getName()) //打印出 henry
console.log(user.constructor) //打印出User(name){this.name=name}这个构造函数
console.log(user.__proto__) //打印出 {getName: ƒ, constructor: ƒ} User.prototype

总结下:

  1. prototype是构造函数的一个属性。
  2. __proto__是实例的属性,一般意义上的【实例的原型】。
  3. 开发人员对构造函数属性prototype的操作,最终会反应到该构造函数的实例上。
  4. 实例上会有constructor属性,指向构造函数

以上,是三者的关系及概念,下面,让我们更清晰的了解他们

首先,我们先看一看以下的内容,方便我们后面的讲解

  1. 你的 JS 代码还没运行的时候,JS 环境里已经有一个 window 对象了
  2. window 对象有一个 Object 属性,window.Object 是一个函数对象
  3. window.Object 这个函数对象有一个重要属性是 prototype
  4. window.Object.prototype 里面有这么几个属性 toString(函数)、valueOf(函数)

然后我们先来看一段代码

var obj = {}
obj.toString()

为什么这段 obj 会有 toString 这个属性呢?

其实这段代码是这样的:

让 obj 变量指向一个空对象,这个空对象有个 __proto__ 属性指向 window.Object.prototype

这样你在调用 obj.toString() 的时候,obj 本身没有 toString,就去 obj.__proro__ 上面去找 toString。

所以你调用 obj.toString 的时候,实际上调用的是 window.Object.prototype.toString

那么 window.Object.prototype.toString 是怎么获取 obj 的内容的呢?

那是因为 obj.toString() 等价于 obj.toString.call(obj), 同时 obj.toString.call(obj) 等价于 window.Object.prototype.toString.call(obj),这句话把 obj 传给 toString 了。

同样的,我们来看下数组

var arr = []
arr.push(1) // [1]

和上面一样,我们解析下这段代码:

让 arr 指向一个空数组,然后 arr.__proto__ 指向 window.Array.prototype

这样你在调用 arr.push 的时候,arr 自身没有 push 属性,就去 arr.__proto__ 上找 push

因此 arr.push 实际上是 window.Array.prototype.pusharr.push(1) 等价与 arr.push.call(arr,1)

arr.push.call(arr,1) 等价于 window.Array.prototype.push.call(arr, 1)

再来看看更复杂一点的

var arr = []
arr.valueOf() // []

arr 本身没有 valueOf,于是去 arr.__proto__ 上找

arr.__proto__ 只有 pop、push 也没有 valueOf,于是去 arr.__proto__.__proto__ 上找

arr.__proto__.__proto__ 就是 window.Object.prototype

所以 arr.valueOf 其实就是 window.Object.prototype.valueOf

arr.valueOf()等价于 arr.valueOf.call(arr)

arr.valueOf.call(arr) 等价于 window.Object.prototype.valueOf.call(arr)

其实,JavaScript 很优美很简单,理解了就不再那么复杂了:

prototype 指向一块内存,这个内存里面有共用属性__proto__ 指向同一块内存

prototype 和 proto 的不同点在于 prototype 是构造函数的属性,而 proto 是对象的属性
难点在于……构造函数也是对象! 如果没有 prototype,那么共用属性就没有立足之地 如果没有
proto,那么一个对象就不知道自己的共用属性有哪些。

顺便讲解一下伪数组,比如很多人不懂什么是伪数组,很简单:
如果一个数组的 __proto__ 直接或间接指向 Array.prototye(用到了数组的共用属性),那么就是真数组
如果一个数组的 __proto__ 没有直接或间接指向 Array.prototye,但是拥有部分数组的属性的,那么就是伪数组


*** 最后提醒一句,我们一般说的 原型 是指构造函数的 prototype,实例的 __proto__

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值