Javascript-原型/原型链

OOP 面向对象

对象是什么?为什么要面向对象?面向对象的优势?

特点:迁移更灵活、代码复用性高、高度模块化的体现,例如vue中new Vue()

对象

对象是对单个物体的简单抽象 =>怎么让多个对象产生关联

对象是基础 也是容器(可以承载其他的对象) => 属性、方法、对象

  • 字面量对象:直接通过对象字面量语法创建的对象。
  • 构造函数/函数对象:设计为使用new关键字来创建新对象的函数。
// 简单对象 - 本身开放
let person1 = {
  name: 'Alice',
  age: 18,
  speak: function(name) {
    return `My name id ${name}`
  }
}

// 函数对象 - 构造实例的函数对象
function Person() {
  this.name = 'Alice'
  this.age = 18
  this.speak = function(name) {
    return `My name is ${name}`
  }
}

let person2 = new Person()
// 下面的构造函数可以生成上面的对象,上面的对象是下面的产物
// => ES6 使用class代替function

使用new Person()时,JavaScript会创建一个新的空对象,将其this上下文指向这个新对象,并执行Person函数体中的代码。

构造函数 - 生成对象

  • 需要一个模板
  • 类就是对象模板
  • js对象的本质不是基于类,而是基于构造函数+原型链传递方式 => constructor + prototype

比如上面new Person()出来的对象,Person本身是一个对象(函数对象),所以person2既有构造函数内的属性,又有基本对象的一些默认属性和方法,例如hasOwnProperty。所以叫基于构造函数,又以某种方式(prototype)将原生对象的属性方法传递下来,才构成了对象,不能直接和类画等号。

所以我们创建的对象既具备了constructor的内容,也具备了prototype代代相传的内容

  1. 追问:new的过程发生了什么?new是什么?new的原理?
function Person(){}
const person1 = new Person()
  1. 结构上:使用new操作符时,JavaScript首先会创建一个空对象(即一个新的对象实例)。这个新创建的对象会被用作函数(在这里我们称之为构造函数)内部this的上下文。最终,如果构造函数没有显式返回一个对象,那么这个新创建的对象(即this所引用的对象)会被返回作为new表达式的结果。

  2. 属性上:这个新创建的对象的__proto__属性(在现代JavaScript中更推荐使用Object.getPrototypeOf()来获取,因为__proto__不是一个标准属性,但它是许多JavaScript引擎中实现原型链的方式)会被设置为构造函数的prototype属性的值。这意味着新创建的对象会继承构造函数原型上的属性和方法。

  3. 关系上:在构造函数执行期间,this关键字会被绑定到新创建的对象上。因此,构造函数内部对this的任何赋值或操作都会影响到这个新对象。这是如何设置新对象的属性和方法的关键。

  4. 生命周期上:一旦new操作符开始执行,它会首先创建新对象,然后设置这个新对象的原型,接着将this绑定到这个新对象上,并执行构造函数中的代码。如果构造函数返回了一个对象,那么这个返回的对象将成为new表达式的最终结果;否则,将返回新创建的对象。

  5. 追问:实例化生成的对象彼此之间有没有联系?

    彼此独立,没有关系。既是传参独立,又是属性独立。

  6. 追问:constructor的存在意义是什么?=>构造一类物品的模板

    1. 每个实例对象被创建时,会自动拥有一个证明身份的属性constructor
    2. constructor来自于原型对象,指向了构造函数的引用。

image-20240708221635176

image-20240708221847296

  1. 追问:使用构造函数创建对象有什么问题?怎么优化呢?原型对象又是什么?

    如果多个实例有类似的方法或属性,那么多次使用constructor则是一种资源浪费。那么我们可以把方法写到原型对象上,这样每个实例被创建后都会有这个方法。

原型对象

参考链接http://t.csdnimg.cn/UdUkv

  • 每个实例创建时都具备一个constructor
  • constructor 和 继承属性 来自于new => 形成了传递链条
  • 子类上生成__proto__,将父类的.prototype放入 => 原型链
function Person(){
	this.name = 'Alice'
    this.age = '20'
}

image-20240905061813588

在JavaScript中,当定义一个函数(如Person)时,该函数自动拥有一个prototype属性,它指向一个空对象。这个对象(通常称为原型对象)。原型对象上还有一个constructor属性,它指向构造函数本身(即Person)。

let p1 = new Person()

image-20240905071242042

当使用new Person()创建实例时,JavaScript会创建一个空对象,并将其内部[[Prototype]]链接到Person.prototype,然后执行Person函数,将this指向新对象以初始化其属性。这样,实例就可以通过原型链访问到Person.prototype上定义的属性和方法。

  1. 对象与构造函数的关系

    • 使用new Person()创建的p1对象,其内部[[Prototype]]链接到Person.prototype,而不是直接链接到Person构造函数。这意味着p1Person构造函数在对象创建后不再直接“关联”,但p1可以通过其原型链间接访问到Person.prototype上的属性和方法。
  2. 多个对象共享原型

    • 通过new Person()创建的多个对象(如p1, p2, p3...)都会共享同一个Person.prototype对象。这意味着你可以在Person.prototype上添加方法和属性,这些方法和属性将自动对所有实例可用。
  3. 属性查找机制

    • 当访问一个对象的属性时(如p1.name),JavaScript会首先在该对象本身查找该属性。如果找不到,它会继续在该对象的原型(Person.prototype)上查找,依此类推,直到找到属性或到达原型链的顶端(null)。
  4. 属性屏蔽

    • 如果在对象上直接定义了一个与原型中同名的属性(如p2.name = "Blone";),则这个新属性会“屏蔽”原型中的同名属性。此时,通过该对象访问该属性将返回对象上定义的值,而不是原型中的值。
  5. 修改属性的行为

    • 当给对象添加或修改一个属性时(如p1.name = "Alan";),实际上是在对象本身上创建或更新了一个属性,而不是修改原型上的属性。这意味着其他实例(如p2)不会受到影响,它们访问的仍然是原型上的旧值(如果之前未在它们自身上定义同名属性的话)。

简化上面描述后就是

  • 对象通过其[[Prototype]]链接到构造函数的原型对象,而非构造函数本身。
  • 多个实例共享同一个原型对象,允许在原型上定义共享的方法和属性。
  • 属性查找从对象本身开始,逐级向上至原型链顶端。
  • 对象上直接定义的属性会屏蔽原型上的同名属性。
  • 修改对象属性实际上是在对象上创建或更新属性,不会修改原型属性,除非显式地通过原型对象进行修改。

image-20240905080206085

image-20240905073510275

posted @ 2024-09-05 08:03  _Bourbon  阅读(14)  评论(0编辑  收藏  举报