js原型和原型链

js原型:

1、原型诞生的目的是什么呢?

js原型的产生是为了解决在js对象实例之间共享属性和方法,并把他们很好聚集在一起(原型对象上)。每个函数都会创建一个prototype属性,这个属性指向的就是原型对象。

2、理解原型

无论何时,只要创建一个函数,就会为这个函数添加一个属性prototype,这个属性指向一个对象------原型对象。原型对象默认只有一个属性constructor(不可枚举属性),这个属性指回构造函数,另外其他的属性和方法都继承自Object.prototype。

构造函数通过new关键字调用创建对象实例。对象实例会有一个[[prototype]]属性指向函数的原型对象,js访问的话就是通过obj.__proto__指向构造函数原型对象。

注意:对象实例和构造函数之间没有直接的联系。但是通过对象实例obj.constructor是指向构造函数的,这是因为对象实例本身是没有constructor这个属性的,但是沿着原型链查找会找到原型上constructor是指向构造函数的。

关系图如上。

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

person.__proto__ === Person.prototype //true

可以看出,构造函数和函数原型是循环引用的关系。

3、几个关于原型的方法:

isPrototypeOf(): 对象实例上的方法,继承自Object.prototype

javascript 复制代码
Person.prototype.isPrototypeOf(person)    //true

Object.getPrototypeOf():可以方便的获取一个对象的原型。

javascript 复制代码
Object.getPrototypeOf(person)===Person.prototype  //true

**Object.setPrototypeOf()**和Object.create():设置对象的原型

1. Object.setPrototypeOf(obj, prototype)

  • 功能: 用于设置一个现有对象的原型。

  • 参数 :

    • obj: 目标对象,其原型将被修改。
    • prototype: 要设置为目标对象原型的新对象(或 null)。
  • 返回值 : 返回被修改的对象 obj

  • 用法 :

    javascript 复制代码
    const obj = {};
    const proto = { greet: () => console.log('Hello!') };
    
    Object.setPrototypeOf(obj, proto);
    
    obj.greet(); // 输出: Hello!
  • 特点 :

    • 修改的是现有对象的原型。
    • 如果目标对象的原型已经被设置为某个值,Object.setPrototypeOf() 会覆盖它。
    • 可能会影响性能,因为设置原型会破坏某些 JavaScript 引擎的优化。

2. Object.create(proto, [propertiesObject])

  • 功能: 创建一个新对象,并将该对象的原型设置为指定的对象。

  • 参数 :

    • proto: 新对象的原型对象(或 null)。
    • propertiesObject(可选): 一个对象,其属性描述符用于定义新对象的自身属性。
  • 返回值 : 返回一个新对象,其原型是 proto

  • 用法 :

    javascript 复制代码
    const proto = { greet: () => console.log('Hello!') };
    const obj = Object.create(proto);
    
    obj.greet(); // 输出: Hello!
  • 特点 :

    • 创建的是新对象,不会修改现有对象。
    • 更适合用于继承或创建具有特定原型的对象。
    • 性能通常优于 Object.setPrototypeOf(),因为它是专门为创建对象而设计的。

4、原型对象属性和对象实例属性的优先级

如果对象实例上定义了和原型对象上同名的属性,那么对象实例的属性会覆盖掉原型对象上的属性。

hasOwnProperty():对象实例上继承自Object.prototype上的方法。区分属性是在对象原型上还是对象实例上。

javascript 复制代码
const obj = Object.create({ inheritedProp: 'value' });
obj.ownProp = 'myValue';

console.log(obj.hasOwnProperty('ownProp')); // true
console.log(obj.hasOwnProperty('inheritedProp')); // false

in操作符 :当你需要检查属性是否存在于对象中(无论是自身属性还是继承属性)时,使用 in

javascript 复制代码
const obj = Object.create({ inheritedProp: 'value' });
obj.ownProp = 'myValue';

console.log('ownProp' in obj); // true
console.log('inheritedProp' in obj); // true

5、关于自定义原型特别需要注意的问题

A)重写原型会切断原型对象和构造函数之间的联系,需要手动重新建立。给原型对象增加constructor属性(且不可枚举),指回构造函数。

javascript 复制代码
function Person(name,age){
  this.name=name;
  this.age=age;
}

Person.prototype={
  name:'myName',
  age:99,
  job:'myjob',
  sayName(){
    console.log(this.name)
  }
}

Object.defineProperty(Person.prototype,'constructor',{
  value:Person
})

B)重写原型之前,使用构造方法创建出来的对象实例的__proto__指向的还是原来的原型对象。重写原型之后,再次创建出来的对象__proto__指向的才是新的原型对象。

javascript 复制代码
function Person(name,age){
  this.name=name;
  this.age=age;
}

let person1=new Person('zhangsan',18)

Person.prototype={
  name:'myName',
  age:99,
  job:'myjob',
  sayName(){
    console.log(this.name)
  }
}

Object.defineProperty(Person.prototype,'constructor',{
  value:Person
})

let person2=new Person('lisi',19)

console.log(person1)
console.log(person2)

根据需要,可以将先前的创建对象实例的原型重新指定一下。

6、原型存在的问题

共享引用值的问题,如下:

javascript 复制代码
function Person(name,age){
  this.name=name;
  this.age=age;
}
Person.prototype.friends=['a','b','c','d']

let person1=new Person('zhangsan',18)
let person2=new Person('lisi',19)

person1.friends.splice(1,1)
console.log(person1.friends)
console.log(person2.friends)

person1的friends少了一个,person2也跟着少了一个,像这种问题其实也不算问题。把friends属性变成对象实例自有的属性即可。

原型链:

1、理解原型链

理解了原型,原型链就很好理解了。其实呢,就是在你访问对象实例的属性时,查找属性的一条链路。先上图:

当你从对象实例person上访问某个属性时,先查找自有属性,然后到函数原型Person.prototype上查找,最后再到函数原型Object.prototype上查找,如果还是找不到,就会返回undefined或者报错。

2、理解Function和Object的关系:

内容较多,且难以理解。我将再写篇文章进行详细介绍,敬请关注。

一文搞懂JS中Function和Object的关系-CSDN博客

相关推荐
菜鸟码农_Shi8 分钟前
Node.js 如何实现 GitHub 登录(OAuth 2.0)
javascript·node.js
没资格抱怨12 分钟前
如何在vue3项目中使用 AbortController取消axios请求
前端·javascript·vue.js
总之就是非常可爱38 分钟前
🚀 使用 ReadableStream 优雅地处理 SSE(Server-Sent Events)
前端·javascript·后端
ᖰ・◡・ᖳ1 小时前
Web APIs阶段
开发语言·前端·javascript·学习
stoneSkySpace1 小时前
算法——BFS
前端·javascript·算法
H5开发新纪元1 小时前
基于 Vue3 + TypeScript + Vite 的现代化移动端应用架构实践
前端·javascript
快乐的小前端1 小时前
class 类基础知识
前端·javascript
kovli2 小时前
红宝书第十讲:「构造函数与原型链」入门及深入解读:用举例子+图画理解“套娃继承”
前端·javascript
就是我2 小时前
如何用lazy+ Suspense实现组件延迟加载
javascript·react native·react.js
懋学的前端攻城狮2 小时前
分布式Node.js--03-扩展
javascript·后端·node.js