js构造函数 原型 原型对象关系


JS中没有类的概念的,主要通过原型链来实现继承。继承意味着复制,然而 JavaScript默认并不会复制对象的属性,JavaScript只是在两个对象之间创建一个关联(原型对象指针),这样,一个对象就可以通过委托访问另一个对象的属性和函数。

原型
当我们 new 了一个新的对象实例,明明什么都没有做,就直接可以访问 toString 、valueOf 等原生方法。那么这些方法是从原型来的。

在控制台打印一个空对象时,我们可以看到,有很多方法,已经“初始化”挂载在内置的 proto 对象上了。这个内置的 proto 是一个指向原型对象的指针,它会在创建一个新的引用类型对象时(显示或者隐式)自动创建,并挂载到新实例上。当我们尝试访问实例对象上的某一属性 / 方法时,如果实例对象上有该属性 / 方法时,就返回实例属性 / 方法,如果没有,就去 proto 指向的原型对象上查找对应的属性 / 方法。这就是为什么我们尝试访问空对象的 toString 和 valueOf 等方法依旧能访问到的原因,JavaScript 正式以这种方式为基础来实现继承的

构造函数
如果说实例的 proto 只是一个指向原型对象的指针,那就说明在此之前原型对象就已经创建了,那么原型对象是什么时候被创建的呢?这就要引入构造函数的概念。
其实构造函数也就只是一个普通的函数而已,如果这个函数可以使用 new 关键字来创建它的实例对象,那么我们就把这种函数称为 构造函数。

// 普通函数
function person () {}

// 构造函数,函数首字母通常大写
function Person () {}
const person = new Person();

原型对象正是在构造函数被声明时一同创建的。构造函数被声明时,原型对象也一同完成创建,然后挂载到构造函数的 prototype 属性上:

原型对象被创建时,会自动生成一个 constructor 属性,指向创建它的构造函数。这样它俩的关系就被紧密地关联起来了。

原型对象也有自己的 proto ,原型对象的 proto 指向的Object.prototype。 Object.prototype.proto 其实是不存在的,是 null 。这也证明了 Object 是 JavaScript 中数据类型的起源。

原型链
其实上面一张图上,那条被 proto 链接起来的链式关系,就称为原型链。

原型链的作用:原型链如此的重要的原因就在于它决定了 JavaScript 中继承的实现方式。当我们访问一个属性时,查找机制如下:

访问对象实例属性,有则返回,没有就通过 proto 去它的原型对象查找。
原型对象找到即返回,找不到,继续通过原型对象的 proto 查找。
一层一层一直找到 Object.prototype ,如果找到目标属性即返回,找不到就返回 undefined,不会再往下找,因为在往下找 proto 就是 null 了。

作用:

1. 内存效率

当在构造函数内部定义方法时,每次通过构造函数创建新对象时,这些方法都会为每个实例重新创建。如果有成千上万个实例,每个实例都包含相同的方法,这会导致巨大的内存浪费。而将方法定义在原型上,则所有实例共享同一套方法,不需要在每个实例中重复创建,从而节省内存。

2. 继承实现

原型链提供了一种在 JavaScript 中实现继承的机制。通过设置一个构造函数的原型指向另一个构造函数的实例,可以实现属性和方法的继承。这种继承模式允许对象通过原型链访问不属于自己的属性或方法,是实现对象间复用和行为共享的关键。

3. 动态属性查找

如果一个对象实例本身没有某个属性,JavaScript 引擎会沿着原型链向上查找,直到找到该属性或到达原型链的末端。这意味着可以在原型上动态修改或添加属性和方法,而所有基于该原型创建的对象实例都会自动获得这些修改或添加,使得修改共享行为变得非常灵活。

原型和原型链的区别

原型是为了实现对象间的联系,解决构造函数无法数据共享而引入的一个属性,而原型链是一个实现对象间联系即继承的主要方法

可以在构造函数定义方法 为啥还用原型链

节省内存; 实现属性和方法的继承;可实现一个特性重载;

尽管可以在构造函数内定义方法,但这种做法有几个缺点:

-内存浪费:每个实例都会创建方法的副本,造成内存浪费。
-可维护性问题:如果需要更改一个方法,就必须修改每个实例或重新定义构造函数,并且确保新实例使用修改后的构造函数。而原型允许所有实例自动访问修改后的方法,提高了代码的可维护性。
- 动态共享不便:通过构造函数定义的方法或属性不易于动态共享或修改。原型提供了一种机制,通过它可以很容易地在多个对象间共享属性和方法。

const ayyay =new array(); 结合原型链说明这些都经历了哪些节点

当执行 const ayyay = new Array(); 这段代码时,JavaScript 引擎会经历以下步骤,结合原型链的概念说明其中涉及的节点:

  1. 创建一个新的空数组对象并将其分配给 ayyay 变量。

  2. 此时,ayyay 变量指向的数组对象会继承来自 Array.prototype(原型) 的属性和方法。

  3. JavaScript引擎在创建数组对象时,它的原型链是这样的:ayyay --> Array.prototype --> Object.prototype --> null

  4. 原型链中的节点包括:对象-》对象的原型-》原型链的顶端-》原型链的终点

    • ayyay:指向新创建的空数组对象。

    • Array.prototype:包含数组对象共享的方法和属性,例如 push()pop() 等。

    • Object.prototype:位于原型链的顶端,包含所有 JavaScript 对象共享的方法,例如 toString()hasOwnProperty() 等。

    • null:原型链的终点,表示没有更多的原型链可供查找。

所以,当你使用 new Array() 创建一个数组时,该数组对象会沿着原型链继承来自 Array.prototypeObject.prototype 的方法和属性。

所有对象都有原型吗?

不是的,大多数对象都有原型。然而,有一些对象例外,它们并不具有原型。

一个典型的例子是Object.prototype。Object.prototype 是原型链的最顶层,它本身不再继承其他对象,因此不具有原型。其他一些例外对象包括null和undefined。它们也没有原型,因为它们不是实例对象,它们是原始值。

function ClassA{
this.x ='hello'
}
ClassA.prototype.x ='word'  //A的原型上面创建了x属性 以后A的实例都会继承这个属性 除非有同名属性覆盖他
var a = new ClassA() //实例a有自己的属性hello 覆盖了word
a.x='what'
console.log(a.x) //what
delete ax
console.log(a.x) //word
delete a.x //没用的删除 本身都已经没有了
console.log(a.x) //word
a.x= undefined
console.log(a.x) //undefined
在 JavaScript 中,对象通过引用传递,但如果在函数内部对传入的参数进行重新赋值,那么这个参数就会指向一个新的对象,而不再影响原始对象。这个机制导致了函数内部对 obj 的修改只影响新的对象,而不是传进来的原始 object 对象。
//将 obj 重新指向了一个新的对象 {a:2}。这个操作只影响了函数内部的 obj 变量,使其指向一个全新的对象,并不影响原来的 object 对象。这里的 obj 不再指向原始的 object 对象,而是被指向了一个全新的对象 {a:2}。这个赋值操作改变了 obj 的引用,使其指向一个新的内存地址,而原来的 object 对象仍然保持不变。
let object = {a:0}
function fun(obj){
obj.a = 1;
obj = {a:2};
obj.b = 2;
}
fun(object);
console.log(object); //{a:1}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值