目录
一 对象的原型(隐式原型)
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中取到的