深入浅出JS—09 对象原型、函数原型及其关系

一 对象的原型(隐式原型)

1. 定义

JavaScript中每个对象都有一个特殊的内置原型属性 [[prototype]],指向另外一个对象。但是通过Object.getPropertyDescriptors(obj)也无法打印出来原型属性,所以叫做隐式原型,并不是因为该原型不可枚举,而是隐藏起来,要专门方法来调用
在这里插入图片描述

2. 获取

  • obj.__proto__属性,这个是早期浏览器自己添加的,存在一定的兼容性问题
  • Object.getPrototypeOf(obj),通用

3. 作用(沿着原型查找属性)

当我们通过引用对象的一个key来获取value时,实际上会触发[[get]]操作,
1、检查对象上是否有该属性,有的话就使用它
2、如果没有就去对象的原型对象上找,也没有的话去原型对象的原型对象上找,直到顶层原型

const obj = {name: 'xs'};
obj.__proto__.age = 18;

console.log(obj.age) //18 
console.log(obj) //{name: 'xs'} 

/*注意:
1 引用age属性,触发[[get]]操作,会先去obj的属性里找,没找到;之后去obj原型属性对应的对象上去找,找到了,返回改值
2 age不是obj的属性,是它原型对象上的属性
*/

二 函数的原型(显式原型)

1. 定义

函数也是一个对象,所以函数内部也有隐式原型__proto__
此外函数还特有一个显式原型属性prototype,指向另外一个对象
注意显式和隐式原型所指的对象是不同的。对于函数而言,我们着重讨论它的显式原型。

function foo() {}
  • 查看foo函数属性, 可以打印出显式原型属性prototype
console.log(Object.getOwnPropertyDescriprors(foo))
// 结果如下
{
  length: 。。。,  // 参数的个数
  name: 。。。,  // 函数名字
  arguments: 。。。, // 函数参数
  caller: 。。。,
  prototype: { value: {}, writable: true, enumerable: false, configurable: false } 
}
  • 函数原型属性指向一个对象,对象内部有一个constructor属性,又指向该函数
    在这里插入图片描述
console.log(Object.getOwnPropertyDescriprors(foo.prototype))
// 结果如下
{
  constructor: {
    value: [Function: foo], // 指回函数对象
    writable: true,        
    enumerable: false,
    configurable: true
  }
}

2. 获取

显式原型可以直接获取

foo.prototype

3. 作用(为实例绑定共用函数)

❎注意:在显式原型对象上添加参数,并不会被get操作得到,get依然沿着foo的隐式原型找

function foo(){}

foo.prototype.eat = function(){
	console.log('eat')
}

// 调用该函数
foo.eat() // 报错,找不到函数
foo.prototype.eat() // 'eat' 可以找到

那显式原型有什么作用呢?
✅ 当函数作为构造函数时,创建的实例对象的隐式原型指向该函数的显式原型

function foo(){}
const f1 = new foo;//如果不传递参数,也可以不写小括号
const f2 = new foo;

console.log(f1.__proto__ ==== foo.prototype) // true
console.log(f1.__proto__ ==== f2.__proto__ ) // true

因此可以在函数原型对象上绑定函数,那么构造的对象都可以共用该函数了,节省内存

foo.prototype.eat = function() {
	console.log('eat');
}

f1.eat() // eat
f2.eat() // eat

三 函数原型和对象原型的关系

准确来说是构造函数和它所创建的实例对象之间的关系。

function foo() {
}
const f = new foo();

// f.__proto__ 和 foo.prototype相等

对象f是构造函数foo创建的实例对象,上一节讲到,new操作符会进行5步操作,第二步时,会将函数foo.prototype属性赋值给对象的原型属性。所以可以得出结论

实例对象的原型 = 构造函数的原型

在这里插入图片描述
更抽象一点,实例对象f1,f2,f3的隐式原型都指向构造函数Foo的原型对象
在这里插入图片描述

那么为什么打印f1、f2、f3会带有foo这个类的标识呢?这个标识来自哪里呢?
从constructor所指向的function.name中取到的

console.log(f1);
// 输出结果↓
foo {}
// 其中foo表示对象f1属于类foo,这个名字就是从constructor所指向的function.name中取到的

四 总结

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值