js原型和原型链

发布于:2025-04-15 ⋅ 阅读:(30) ⋅ 点赞:(0)

js原型:

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

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

2、理解原型

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

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

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

关系图如上。

Person === Person.prototype.constructor //true

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

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

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

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

Person.prototype.isPrototypeOf(person)    //true

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

Object.getPrototypeOf(person)===Person.prototype  //true

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

1. Object.setPrototypeOf(obj, prototype)

  • 功能: 用于设置一个现有对象的原型。
  • 参数:
    • obj: 目标对象,其原型将被修改。
    • prototype: 要设置为目标对象原型的新对象(或 null)。
  • 返回值: 返回被修改的对象 obj
  • 用法:
    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
  • 用法:
    const proto = { greet: () => console.log('Hello!') };
    const obj = Object.create(proto);
    
    obj.greet(); // 输出: Hello!
  • 特点:
    • 创建的是新对象,不会修改现有对象。
    • 更适合用于继承或创建具有特定原型的对象。
    • 性能通常优于 Object.setPrototypeOf(),因为它是专门为创建对象而设计的。

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

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

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

const obj = Object.create({ inheritedProp: 'value' });
obj.ownProp = 'myValue';

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

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

const obj = Object.create({ inheritedProp: 'value' });
obj.ownProp = 'myValue';

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

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

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

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__指向的才是新的原型对象。

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、原型存在的问题

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

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博客


网站公告

今日签到

点亮在社区的每一天
去签到