详解prototype、__proto__和constructor

之前对prototype、__proto__和constructor理解的不够透彻,今天就好好梳理和总结一下。
本文大纲:
在这里插入图片描述

1、prototype

prototype的英文意思:原型;样本;标准,模范;

1.1 定义

每个函数创建的时候都会自动创建一个prototype属性prototype属性是函数独有的。prototype的含义是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象,由此可知(以下面的为例)

function Person(){} 
let person1 = new Person()
由此可知
person1.__proto__ === Person.prototype  // true 它们两个指向完全一样
1.2 作用

那prototype属性的作用又是什么呢?它的作用就是包含可以由特定类型的所有实例共享的属性和方法,也就是让该函数所实例化得到的对象都可以找到公用的属性和方法
再次强调:任何函数在创建的时候,其实会默认同时创建该函数的prototype对象

1.3 总结

prototype属性是函数独有的,任何函数在创建的时候,其实会默认同时创建该函数的prototype对象,保存着所有实例共享的属性和方法,是所有实例的原型对象。

2、__proto__

2.1 定义

__proto__(写法是两边各有两个下划线_)属性是对象独有的,它其实是[[prototype]]的引用, 每个实例(通过构造函数生成的实例都是对象,函数也是对象,所以函数也有__proto__)上面都有__proto__ 属性,它指向原型对象(构造函数的prototype属性),以下面的代码为例进行说明:

function Person(){} 
let person1 = new Person()
person1.__proto__ === Person.prototype  // true

先强调一下通过new构造函数生成的实例都是对象(可以参考new构造函数之后执行的步骤),所以person1是一个实例对象。也可以这么理解,每个实例上面都有__proto__ 属性
所以person1具有__proto__属性,__proto__属性指向构造函数的prototype(原型对象)
person1.proto === Person.prototype,它俩的指向是一样的。

这里补充说明一下:[[prototype]]属性是隐藏的,属于内部特性,开发者不能直接在JavaScript中直接访问这些特性。不过目前大部分新浏览器实现方式是使用__proto__来表示。

2.2 作用

它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(可以理解为父对象)里找,如果父对象也不存在这个属性,则继续往父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找…直到原型链顶端null(可以理解为原始人。。。),再往上找就相当于在null上取值,会报错(可以理解为,再往上就已经不是“人”的范畴了,找不到了,到此结束,null为原型链的终点),由以上这种通过__proto__属性来连接对象直到null的一条链即为我们所谓的原型链

function Person(){} 
let person1 = new Person()
person1.__proto__ === Person.prototype  // true

原型链实现了继承。
person1中一个属性在本身里面找不到,会到__proto__属性所指向的那个对象(也就是原型对象,可以称为父对象)中找,也就是Person.prototype,Person.prototype是一个对象,所以也有__proto__属性,如果在Person.prototype也找不到,会去Person.prototype.__proto__中寻找,也就是person1.__proto__.__proto__(Person.prototype.__proto__ === person1.__proto__.__proto__),如果还找不到,就继续往上找,Person.prototype.__proto__.__proto__,直到最后Person.prototype.__proto__.__proto__.__proto__ === null,也就是person1.__proto__.__proto__.__proto__.__proto__ === null
其实我们平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠__proto__继承而来的。

2.3 总结

__proto__属性是对象上的属性,一般都是通过构造函数生成的实例对象所具有的属性,指向构造函数的prototype的属性(原型对象),也可以说指向了共享对象,即实例的原型对象(共享对象)。所以可以说__proto__属性就是原型对象(prototype),原型对象本身也有__proto__,指向Object.prototype,最后Object.prototype.__proto__指向了null。null是原型链的顶端。
补充:绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.proto 时,可以理解成返回了 Object.getPrototypeOf(obj)。

3、constructor

3.1 定义

单从constructor这个属性来讲,只有prototype对象才有,每个函数在创建的时候,JS会同时创建一个该函数对应的prototype对象,
该函数创建的对象.__proto__ === 该函数.prototype
该函数.prototype.constructor === 该函数本身
故通过函数创建的对象即使自己没有constructor属性,它也能通过__proto__找到对应的constructor,所以任何对象最终都可以找到其构造函数(null如果当成对象的话,将null除外)。
示例如下:

function Person(){} 
let person1 = new Person()

person1对象本身不具有constructor属性,所以会通过__proto__属性到原型链中找,而person1.proto=== Person.prototype,Person.prototype具有constructor属性并指向了Person,所以person1.constructor指向了Person,它不是person1自己本身拥有的,是继承而来的。

从广义上来说:constructor属性是对象才拥有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数,每个对象都有构造函数(本身拥有或继承而来,继承而来的要结合__proto__属性查看会更清楚点),Function这个对象比较特殊,它的构造函数就是它自己(因为Function可以看成是一个函数,也可以是一个对象),所有函数和对象最终都是由Function构造函数得来,所以constructor属性的终点就是Function这个函数。

3.2 作用

constructor属性不影响任何JavaScript的内部属性。instanceof检测对象的原型链,通常你是无法修改的(不过某些引擎通过私有的__proto__属性暴露出来)。

constructor其实没有什么用处,只是JavaScript语言设计的历史遗留物。由于constructor属性是可以变更的,所以未必真的指向对象的构造函数,只是一个提示。不过,从编程习惯上,我们应该尽量让对象的constructor指向其构造函数,以维持这个惯例。

不过在一些场景下,还是很有用的,示例如下:

var person1;
(function(){
  function Person (name) {
    this.name = name;
  }

  Person.prototype.sayName= function () {
    console.log(this.name);
  }
  person1 = new Person('qingyun');
})()
person1.sayName();//qingyun

console.log(Person); //Father is not defined

因为Person在闭包中,当我们想对Person类增加方法时可以通过

person1.constructor.prototype.sayAge = function(age){
    console.log(age);
}
person1.sayAge('20'); //20

有人说,也可以直接这样写

person1.__proto__.sayAge = function(age){
    console.log(age);
}
person1.sayAge('20'); //20

__proto__这个属性是浏览器厂商自己内置的,有的浏览器是不支持的,所以使用constructor更可靠一点。

3.3 总结

注意:单从constructor这个属性来讲,只有prototype对象才有。每个函数在创建的时候,JS会同时创建一个该函数对应的prototype对象,而函数创建的对象.proto === 该函数.prototype,该函数.prototype.constructor===该函数本身。故通过函数创建的对象即使自己没有constructor属性,它也能通过__proto__找到对应的constructor,所以任何对象最终都可以找到其构造函数(null如果当成对象的话,将null除外)。记住,单从constructor这个属性来讲,只有prototype对象才有,所以才能实现继承。

最后,感谢这篇博文,本文中的部分内容参考自这篇博文:
帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值