js对象继承

本文详细讲解了JavaScript中的实例对象、原型对象、构造函数、__proto__、prototype和constructor的关系,包括原型链继承、构造函数继承以及组合继承的解决方案。通过实例演示了如何避免引用值共享问题和调用原型方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 基本概念

实例对象: 通过实例的方式(new)构造出来的对象
原型对象:所有实例的公共祖先,在实例对象上可以得到原型对象的属性和方法
构造函数:可以通过new关键子执行的函数

//Foo是一个构造函数
function Foo(){
     this.a = '1'
  }
//foo 是通过new构造的一个实例对象
var foo = new Foo()

2. prototype 、proto 、constructor之间的关系

prototype即原型,原型的访问方式有
(1)通过构造函数的方式 Foo.prototype,该方法适合修改原型
(2)通过实例的方式 foo.proto
(3)ES6方式 Object.getprototypeOf(foo)

function Foo(){
        this.a = 1
  }
 //原型的重写
 Foo.prototype = {   //这是构造函数的原型
       aa: 10,
       bb: function(){
       console.log(20);
        }
  }
var foo = new Foo();  //这是一个实例对象
console.log(foo.aa);  //10 ,实例对象可以拿到原型的属性
console.log(foo.a);   //1
console.log(foo);
console.log(foo.__proto__);  //获得其原型对象{aa: 10, bb: ƒ}

constructor 是原型上的一个属性,它指向的是构造函数本身

//例如
function Foo(){
    this.a = 1
}
var foo = new Foo();  //这是一个实例对象
console.log(foo.__proto__);  //获得该实例的原型队形

输出结果:
{constructor: ƒ}

console.log(Foo.prototype.constructor === Foo); //true
一旦更改原型,constructor改变

//重写原型
 Foo.prototype = {   //这是构造函数的原型
            aa: 10,
            bb: function(){
                console.log(20);
            }
        }
var foo = new Foo();  //这是一个实例
console.log(Foo.prototype.constructor);

输出
ƒ Object()
原型链为:
在这里插入图片描述
Foo 是本身没有constructor属性 ——(父亲)原型 Prototype,依旧没有constructor属性 —— 爷爷prototype constructot 属性值是Object
console.log(Foo.prototype.constructor);
根据原型链搜索
Foo重写后没有 constructor 然后找它的父亲,仍然没有constructor ,再找他爷爷,找到了,原型链继承得到。

注意:
当重写原型时,为其加上constructor属性,指向这个构造函数

 Foo.prototype = {   //这是构造函数的原型
            aa: 10,
            bb: function(){
                console.log(20);
            }
            constructor: Foo
        }

总结:
prototype和__proto__都是指向的原型,只是访问方法不同
constructot 指向的时构造函数

3. 对象的继承

  1. 原型链继承

    根据原型链继承——实例对象可以拿到原型对象的属性和方法——也能拿到原型的原型的属性和方法,一级级继承得到。

<script>
        function Super() {
            this.a = '1'

            }
        Super.prototype.say = function(){
            console.log(222);
        }
        function Sub(){
        }
        //Sub的原型是一个Super实例,所以Sub.prototype本身具有了属性a,同时可以访问其原型Super.prototype的方法
        Sub.prototype = new Super();

        var sub1 = new Sub();  //它具有原型
        console.log(sub1.a);
        console.log(sub1.say);

    </script>

解释:(—>表示原型是)
实例对象sub1—>Sub.prototype(只是一个new super实例对象,所以可以拿到实例上的属性),并且通过Super.prototypy可以访问Super 的原型对象方法

出现问题:
引用值(String,Object类型)共享问题)

 function Super() {
     this.a = [1,2,3]
}

//在sub1.a的属性值中加入新的数
 sub1.a.push(4)
console.log(sup.a); //[1, 2, 3]
console.log(sub1.a);  //[1, 2, 3, 4]
console.log(sub2.a);  //[1, 2, 3, 4]
  1. 构造函数继承——解决引用值的共享问题

此继承存在的问题:没办法拿到原型上的方法

<script>
        function Super() {
            this.a = [1, 2, 3]
            }
        Super.prototype.say = function () {
                console.log(222);
            }
        function Sub() {
            Super.call(this);  //这里的this指向创建的Sub对象
            }
            
            //实现互不影响
        var sub1 = new Sub();
        // 执行过程,sub1为一个实例对象,在Sub构造函数中,this指向这个对象,
        // 执行Super.call(this) === Super(sub1) 到函数(此处没有和new 搭配使用
        //  ,其实是个普通函数吧,所有没发拿到原型上的方法)内执行 this.a === sub1.a
        var sub2 = new Sub();
          // 执行过程,sub2为一个实例对象,在Sub构造函数中,this指向这个对象,
        // 执行Super.call(this) === Super(sub2) 到函数内执行 this.a === sub2.a
    </script>
  1. 组合继承的方式(原型链与构造数组的结合)又叫伪经典继承
    组合其优点
    (1) 解决引用值的共享问题
    (2) 解决原型方法和属性调用问题
<script>
        function Super() {
            this.a = [1, 2, 3]
        }
        Super.prototype.say = function () {
            console.log(222);
        }

        function Sub() {
            Super.call(this);  //这里的this指向创建的Sub对象
        }

        // 新增——原型链关键语句
        Sub.prototype = new Super();
        //每次创建Sub实例对象,都会调用两次Super函数
        var sub1 = new Sub();
        var sub2 = new Sub();
</script>
### JavaScript 对象继承的实现方法 JavaScript对象继承主要依赖于原型链机制。尽管 ES6 引入了 `class` 关键字来简化语法,但实际上仍然基于原型链工作[^1]。 #### 原型链继承JavaScript 中,每个函数都有一个默认的 `prototype` 属性,它指向一个对象。当创建实例时,这个实例会有一个内部指针(通常称为 `__proto__`),指向其构造函数的 `prototype` 对象。这种关联构成了所谓的“原型链”。 以下是通过原型链实现继承的一个简单例子: ```javascript function Parent() { this.name = "Parent"; } Parent.prototype.sayName = function () { console.log(this.name); }; function Child() {} // 设置 Child 的 prototype 为 Parent 的实例 Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; // 恢复 constructor 指向 const childInstance = new Child(); childInstance.sayName(); // 输出 undefined, 因为 name 不属于子类实例 ``` 在这个例子中,`Child` 继承自 `Parent`,因此可以访问到定义在 `Parent.prototype` 上的方法 `sayName()`。 #### 使用 `Object.setPrototypeOf` 方法 另一种设置原型关系的方式是利用 `Object.setPrototypeOf` 函数。这种方式更加直观,但它并不推荐用于生产环境,因为可能会影响性能。 下面是一个简单的示例: ```javascript function Animal(name) { this.name = name; } Animal.prototype.speak = function () { console.log(`${this.name} makes a noise.`); }; function Dog(name) { Animal.call(this, name); // 调用父类构造器 } Object.setPrototypeOf(Dog.prototype, Animal.prototype); Dog.prototype.bark = function () { console.log(`${this.name} barks!`); }; let myDog = new Dog('Rex'); myDog.speak(); // Rex makes a noise. myDog.bark(); // Rex barks! ``` 这里我们手动设置了 `Dog.prototype` 的原型为 `Animal.prototype`,从而实现了继承。 #### 类 (Class) 表达式的使用 随着 ECMAScript 2015 (ES6) 的发布,JavaScript 提供了一种更接近传统面向对象语言风格的方式来表达继承——即使用 `class` 和 `extends` 关键词。 下面是相同的逻辑改写成现代语法的形式: ```javascript class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a sound.`); } } class Dog extends Animal { bark() { console.log(`${this.name} barks!`); } } const rex = new Dog('Rex'); rex.speak(); // Rex makes a sound. rex.bark(); // Rex barks! ``` 这种方法不仅语义清晰而且易于理解,在实际开发中被广泛采用[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值