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 = 18this.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代代相传的内容
- 追问:new的过程发生了什么?new是什么?new的原理?
function Person(){}
const person1 = new Person()
-
结构上:使用
new
操作符时,JavaScript首先会创建一个空对象(即一个新的对象实例)。这个新创建的对象会被用作函数(在这里我们称之为构造函数)内部this
的上下文。最终,如果构造函数没有显式返回一个对象,那么这个新创建的对象(即this
所引用的对象)会被返回作为new
表达式的结果。 -
属性上:这个新创建的对象的
__proto__
属性(在现代JavaScript中更推荐使用Object.getPrototypeOf()
来获取,因为__proto__
不是一个标准属性,但它是许多JavaScript引擎中实现原型链的方式)会被设置为构造函数的prototype
属性的值。这意味着新创建的对象会继承构造函数原型上的属性和方法。 -
关系上:在构造函数执行期间,
this
关键字会被绑定到新创建的对象上。因此,构造函数内部对this
的任何赋值或操作都会影响到这个新对象。这是如何设置新对象的属性和方法的关键。 -
生命周期上:一旦
new
操作符开始执行,它会首先创建新对象,然后设置这个新对象的原型,接着将this
绑定到这个新对象上,并执行构造函数中的代码
。如果构造函数返回了一个对象,那么这个返回的对象将成为new
表达式的最终结果;否则,将返回新创建的对象。 -
追问:实例化生成的对象彼此之间有没有联系?
彼此独立,没有关系。既是传参独立,又是属性独立。
-
追问:constructor的存在意义是什么?=>构造一类物品的模板
- 每个实例对象被创建时,会自动拥有一个证明身份的属性constructor
- constructor来自于原型对象,指向了构造函数的引用。
-
追问:使用构造函数创建对象有什么问题?怎么优化呢?原型对象又是什么?
如果多个实例有类似的方法或属性,那么多次使用
constructor
则是一种资源浪费。那么我们可以把方法写到原型对象上,这样每个实例被创建后都会有这个方法。
原型对象
参考链接http://t.csdnimg.cn/UdUkv
- 每个实例创建时都具备一个constructor
- constructor 和 继承属性 来自于new => 形成了传递链条
- 子类上生成
__proto__
,将父类的.prototype
放入 => 原型链
function Person(){this.name = 'Alice'this.age = '20'
}
在JavaScript中,当定义一个函数(如Person)时,该函数自动拥有一个prototype属性,它指向一个空对象。这个对象(通常称为原型对象)。原型对象上还有一个constructor属性,它指向构造函数本身(即Person)。
let p1 = new Person()
当使用new Person()创建实例时,JavaScript会创建一个空对象,并将其内部[[Prototype]]链接到Person.prototype,然后执行Person函数,将this指向新对象以初始化其属性。这样,实例就可以通过原型链访问到Person.prototype上定义的属性和方法。
-
对象与构造函数的关系:
- 使用
new Person()
创建的p1
对象,其内部[[Prototype]]
链接到Person.prototype
,而不是直接链接到Person
构造函数。这意味着p1
与Person
构造函数在对象创建后不再直接“关联”,但p1
可以通过其原型链间接访问到Person.prototype
上的属性和方法。
- 使用
-
多个对象共享原型:
- 通过
new Person()
创建的多个对象(如p1
,p2
,p3
...)都会共享同一个Person.prototype
对象。这意味着你可以在Person.prototype
上添加方法和属性,这些方法和属性将自动对所有实例可用。
- 通过
-
属性查找机制:
- 当访问一个对象的属性时(如
p1.name
),JavaScript会首先在该对象本身查找该属性。如果找不到,它会继续在该对象的原型(Person.prototype
)上查找,依此类推,直到找到属性或到达原型链的顶端(null
)。
- 当访问一个对象的属性时(如
-
属性屏蔽:
- 如果在对象上直接定义了一个与原型中同名的属性(如
p2.name = "Blone";
),则这个新属性会“屏蔽”原型中的同名属性。此时,通过该对象访问该属性将返回对象上定义的值,而不是原型中的值。
- 如果在对象上直接定义了一个与原型中同名的属性(如
-
修改属性的行为:
- 当给对象添加或修改一个属性时(如
p1.name = "Alan";
),实际上是在对象本身上创建或更新了一个属性,而不是修改原型上的属性。这意味着其他实例(如p2
)不会受到影响,它们访问的仍然是原型上的旧值(如果之前未在它们自身上定义同名属性的话)。
- 当给对象添加或修改一个属性时(如
简化上面描述后就是:
- 对象通过其
[[Prototype]]
链接到构造函数的原型对象,而非构造函数本身。 - 多个实例共享同一个原型对象,允许在原型上定义共享的方法和属性。
- 属性查找从对象本身开始,逐级向上至原型链顶端。
- 对象上直接定义的属性会屏蔽原型上的同名属性。
- 修改对象属性实际上是在对象上创建或更新属性,不会修改原型属性,除非显式地通过原型对象进行修改。