JavaScript---原型和原型链

目录

一、引用类型皆为对象

二、原型和原型链是什么

三、__proto__与prototype

总结

四、原型链顶层

五、constructor

六、函数对象的原型链


一、引用类型皆为对象

原型原型链 都是来源于对象 而服务于对象

  • JavaScript中一切引用类型都是对象,对象就是属性的集合
  • Array、Function、Object、Date、RegExp都是引用类型
  • 数组是对象、函数是对象、正则是对象、对象还是对象
javascript 复制代码
const arr = []
const fn = function() {}
const obj = {}

console.log(typeof arr) // "object"
console.log(fn instanceof Object) // true
console.log(typeof obj) // "object"

二、原型和原型链是什么

每一个对象从被创建开始就和另一个对象关联,从另一个对象上继承其属性,这个"另一个对象"就是"原型"

当访问一个对象的属性时,先在对象的本身查找,找不到就去对象的原型上找,如果还找不到,就去对象的原型的原型上去找,如此重复,直到找到为止,或者查找到最顶层的原型对象中也没有找到,就结束查找,返回undefined

而在对象的原型上,依次查找时,所遍历的所有原型,叫作"原型链"

javascript 复制代码
const obj = {}
obj.sayHello = () => {console.log('Hello')}
console.log(obj.hasOwnProperty('sayHello')) // true
  • 上面代码中,我们访问obj.hasOwnProperty()时,发现该对象并没有这个方法,但是我们仍然可以使用,这是因为在obj对象的原型上,存在该方法,所以我们可以调用
  • 到现在,我们可以理解数组可以使用push、slice等方法,函数可以使用call、bind方法了,因为在它们的原型链上找到了对应的方法

三、__proto__与prototype

每个对象都有原型,我们怎么获取到一个对象的原型呢?可以使用对象的"proto"属性,指向对象的原型

  • 所有引用类型都有__proto__属性
  • proto 属性在ES6中不推荐被使用,现在更推荐使用Obejct.getPrototypeOf(),该方法也可以获取到对象的属性
javascript 复制代码
console.log(Object.getPrototypeOf(obj) === obj.__proto__) // true

构造函数 是为了创建特定类型的对象,那如果我想让"Person"这个构造函数创建的对象都共享一个方法,不能像下面这样:

错误示范:

javascript 复制代码
function Person(name){
    this.name = name;
}
// 调用构造函数Person创建一个新对象PersonA
const personA = new Person();
// 给PersonA的原型添加一个方法,以供之后Person创建的对象共享
personA.__proto__.sayHello = function(){
    console.log(`Hello, my name is ${this.name}`);
}
// 创建一个新的Person对象PersonB
const personB = new Person('Bob');
// 调用PersonB的sayHello方法
personB.sayHello(); // Hello, my name is Bob
  • 当我们不想修改构造函数创建的对象,并且还不想修改构造函数时,如果先通过构造函数创建一个对象,再通过对象的原型修改,这样确实可以,但实在太过啰嗦

为此,我们可以通过"prototype"来直接修改原型,每个函数多拥有prototype属性,指向使用new操作符和该函数创建的对象的实例的原型对象

javascript 复制代码
console.log(personA.__proto__ === Person.prototype); // true
console.log(personB.__proto__ === Person.prototype); // true

正确示范:

javascript 复制代码
function Person(name){
    this.name = name;
}
Person.prototype.sayHello = function(){
    console.log(`Hello, my name is ${this.name}`);
}
const personA = new Person('Bob');
personA.sayHello(); // Hello, my name is Bob

总结

  • 对象有**proto属性(Object.create(null)除外 ),函数有proto属性,数组也有proto属性,只要是引用类型** ,就有**proto属性,指向其原型**
  • 只有函数有prototype属性,指向new操作符加调用该函数创建的对象实例的原型对象

四、原型链顶层

原型链 之所以叫原型链 ,不叫原型环 ,说明它是有始有终的,那么原型链的顶层是什么呢?

personA对象的原型对象,很简单:

javascript 复制代码
console.log(personA.__proto__ === Person.prototype); 

接着往上找,Person.prototype 也是一个普通对象,可以理解为Object构造函数创建的,所以得出下面结论:

javascript 复制代码
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Person.prototype.__proto__)
  • 上图红框内的**proto** 就是Person.prototype.proto,也就是下图:

Object.protype 也是一个对象,那么它的原型呢?是空值null

javascript 复制代码
Object.prototype.__proto__ === null

五、constructor

构造函数都有一个prototype属性,指向使用这个构造函数创建的对象实例的原型对象

这个原型对象中默认有一个constructor属性,指回该构造函数

javascript 复制代码
Person.prototype.constructor === Person // true

六、函数对象的原型链

函数也是对象,故函数也有**proto**属性,这里比较容易混淆

因为函数既有prototype 也有**proto**,很容易搞晕

我们只需要记住:

  • 函数的prototype 使用的前提是:"函数被作为构造函数使用,此时prototype指向构造函数创建的对象实例的__proto__原型对象"
  • 函数的**proto** 指的是:"函数被当做一个普通对象使用,此时它的__proto__指向Function.prototype,因为Function是所有函数的构造函数"
  • 一个特例,Fyunction.__prototype === Function.prototype,记住就好
javascript 复制代码
console.log(Person.__proto__ === Function.prototype) // true
console.log(Person.__proto__.constructor === Function) // true
console.log(Person.__proto__.__proto__ === Object.prototype) // true

有点绕,但是理解这三个代码,相信**proto** 和prototype你永远不会弄混了

相关推荐
忆柒几秒前
大模型微调
前端
程序员小续几秒前
TypeScript中any、unknown、never的区别
前端·面试·typescript
蘑菇头爱平底锅3 分钟前
数字孪生-DTS-孪创城市-古城分布
前端·数据可视化
花花鱼5 分钟前
js中 剩余运算符(Rest Operator )(...)和展开运算符(Spread Operator)(...)的区别及用法
开发语言·javascript·ecmascript
sxf3596 分钟前
vue项目调用netcore webapi接口提示:400 Bad Request的解决
java·前端·vue.js
泽55318027 分钟前
4.13日总结
开发语言·python·pycharm
咪库咪库咪34 分钟前
CSS过渡与动画
前端
前端康师傅38 分钟前
CSS基础教程-性能优化
前端·css
掘金用户897242 分钟前
微信小程序 扫码+拍照
前端
一颗奇趣蛋42 分钟前
input输入框输入数字之后展示千分位(财务系统专用)
javascript·vue.js