十一、原型与原型链深入、对象继承、call、apply

本文详细探讨了JavaScript中的原型和原型链,解释了如何查找对象的属性并分析了原型链的顶端。此外,还讨论了对象继承的不同方式,如`Object.create()`,并指出并非所有对象都继承`Object.prototype`。文章进一步讲解了`call()`和`apply()`方法的用途,展示了如何在不同上下文中改变`this`的指向,以及它们在实现继承和组合功能中的作用。

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

十一、原型与原型链深入、对象继承、call、apply

原型与原型链深入

前导知识

几乎所有对象都有自己的原型,包括原型对象也有自己的原型。

例外:

var obj = Object.create(null);
console.log(obj);

在这里插入图片描述

原型

Prototype

构造函数的原型对象是实例化对象的公共祖先,实例化的不同对象能继承原型中的属性和方法。

构造函数的prototype可以手动设置吗? √

Professor.prototype.tSkill = 'Java';
function Professor(){}
var professor = new Professor();

Teacher.prototype = professor;
function Teacher(){
    this.mkill = 'JS/JQ';
}
var teacher = new Teacher();

Student.prototype = teacher;
function Student(){
    this.pkill = 'HTML/CSS';
}
var student = new Student();

console.log(student);
console.log(student.pkill, student.mkill, student.tSkill);
//HTML/CSS JS/JQ Java

在这里插入图片描述
当我们在student对象上寻找属性时,找不到就去它的构造函数的原型对象上teacher上寻找,若找不到,继续向teacher对象的构造函数的原型对象professor上找,以此类推

原型链

对象沿着__proto__在原型上寻找属性就形成了一个链条式的继承关系,这个继承关系,我们就叫做原型链。

原型链有顶端吗? -> Object.prototype
console.log(Professor.prototype);

在这里插入图片描述
Object.prototype下保存了一个toString方法。

实例化对象只能查看原型链上的prototype吗?×
function Teacher(){
    this.mkill = 'JS/JQ';
    this.success = {
        alibaba: '28',
        tencent: '30',
        jingdong: '11'
    }
    this.students = 500;
}
var teacher = new Teacher();

Student.prototype = teacher;
function Student(){
    this.pkill = 'HTML/CSS';
}
var student = new Student();

当原型的某个属性值是引用值类型:

  • 实例化对象:学生
  • 原型对象:老师

不推荐这样使用

通过学生增加老师的属性可以吗?√ 学生的属性会增加吗?×

通过学生更改老师的属性可以吗?√ 学生的属性会增加吗?×

通过学生删除老师的属性可以吗?√

student.success.baidu = '100';
student.success.alibaba = '29';
delete student.success.jindong;
console.log(teacher, student);

在这里插入图片描述

当原型的某个属性是原始值类型:

通过学生增加老师的属性可以吗?× 学生的属性会增加吗?√

student.students++;
console.log(teacher, student);

在这里插入图片描述

这个过程相当于:

student.students = student.students + 1;

student.students在原型链中可以访问到,值为500,+1为501;

然后在实例化对象中增加一个student属性,属性名为students,赋值为501;

原型对象方法上的this指向谁?

谁使用this,this就指向谁

function Car(){
    this.brand = 'Benz';
}

Car.prototype = {
    brand: 'Mazda',
    intro: function(){
        console.log('我是'+ this.brand + '车');
    }
}

var car = new Car();
car.intro(); // 我是Benz车
Car.prototype.intro(); //我是Mazda车

​ 实例化对象的this指向自己,car.intro()只是借用原型上的方法,当this指向的对象中有这个brand属性时,直接调用;当对象中没有这个属性,通过this指向的对象的__proto__来访问原型对象看原型上有没有这个属性。

​ 如果想打印原型上的属性,就让原型对象去调用自己的方法,this指向原型对象。

对象继承

生成对象的方法

  • 构造器是Object() ->它的原型对象是Object.prototype
    • 字面量方法var obj = {}
    • 系统自带的构造函数new Object()(公司不用这个)
  • 构造器是自己:自定义构造函数
  • object.create(对象/null)

对于自定义构造函数,它的原型对象的原型是由系统自带的Object构造出来的。

Object.create(对象/null)

object.create创建新对象,并指定它的原型对象

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的原型对象

创造一个新对象,并且指定这个对象的原型对象,创建一种继承关系。

function Car(){}
Car.prototype.num = 1;
var car1 = new Car();
var car2 = Object.create(Car.prototype);
console.log(car1);
console.log(car2);

在这里插入图片描述

Object.create(原型对象)创造的对象与通过构造函数实例化对象,他们生成对象的内容是一致的。


new做了什么:

  1. 实例化对象
  2. 调用构造函数的初始化属性和方法
  3. 指定实例对象的原型

object.create()做了什么:

  1. 创建一个新对象
  2. 把别的对象作为创建的对象的原型,从而实现继承
所有的对象都继承Object.prototype?×

原型链的顶端是:Object.prototype

是不是所有对象都继承Object.prototype?×

Object.create(null)创建的对象什么属性都没有!

var obj = Object.create(null);
console.log(obj);

在这里插入图片描述

那能给这个空对象手动指定原型吗?√

那它能访问(继承)手动指定的原型吗?×

var obj = Object.create(null);
var obj1 = {
    count: 2
}
obj.__proto__ = obj1;
console.log(obj.count); //undefined

只有系统指定的原型才能被继承

Object.prototype.toString()方法

先看一种情况:

不是说原始值没有属性吗?怎么还能调用方法?

var num = 1;
console.log(num.toString()); // '1'

console.log(undefined.toString()); //报错
console.log(null.toString()); //报错

对于原始值Number,String,Boolean类型时,在它们调用自身的方法和属性时,系统会把它们包装一下,变成对象。而undefined和null不能经过包装类变成对象。

通过系统内置的构造函数,new构造函数生成一个对象:new Number()

相当于是 new Number(num).toString()

这个toString()是从哪里来的?

打印一下数字对象:

console.log(new Number());

在这里插入图片描述

这个toString方法来自Number.prototype上的方法

那Object.prototype上有toString方法,为什么你数字对象的原型还要有toString方法?

方法是有区别的,在哪?处理的结果是不同的!!!

Object.prototype.toString.call(1);

打印对象类型的Number构造函数

Number.prototype.toString.call(1);

打印’1’

在这里插入图片描述

因为原型链顶端的Object.prototype上实现不了我要的功能,于是在构造函数的prototype上重写toString方法。

document.write()

document.write()隐式转换为String类型

我实验出的:

  • 原始值就直接转成字符串
  • 引用值去找一下它有没有toString方法
var obj = {}
var obj2 = Object.create(null);
document.write(obj); //[object Object]
document.write(obj2);//报错:Cannot convert object to primitive value

页面上显示字面量空对象,会自动调用它所继承的Object.prototype上的toString方法,把其变成String类型。

而Object.create(null)空对象会报错?因为空对象中什么属性都没有,不会继承Object.prototype,更没有toString方法。

那能手动添加toString方法吗?√

 var num = 1;
var obj = {}
var obj2 = Object.create(null);
obj2.toString = function(){
    return 'obj2';
}
document.write(1);
document.write(obj)
document.write(obj2.toString());
document.write(obj2);

在这里插入图片描述

Call() apply()

Call(对象,参数列表)

函数执行时,其实隐式使用call方法();

function test(){
    console.log(1);
}
test(); // 1
test.call(); // 1
它有什么用?

实现继承,一个对象中拿到构造函数所有属性和方法

function Car(brand, color) {
	this.brand = brand;
    this.color = color;
}
var newCar = {};
Car.call(newCar, 'benz', 'black');
console.log(newCar); //{brand: 'benz', color: 'black'}

函数使用call方法指定对象和参数后,改变了this的指向,变成了newCar.brand,newCar.color,对象相当于拿到了构造函数的方法和属性。

apply(对象,参数数组)

call()方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组

开发中我们怎么用call和apply:
  • 在一个对象中拿到其他构造函数中的方法

    形式:在一个构造函数A中,调用另一个构造函数.call(this, 参数列表),然后实例化A,拿到另一个构造函数的属性和方法

    不是在构造函数的方法中实例化另一个对象

    function Car(opt){
        this.displacement = opt.displacement;
        this.color = opt.color;
        this.brand = opt.brand;
    }
    function Person(opt){
        //首行调用
        Car.call(this, opt.carOpt);
        this.name = opt.name;
        this.age = opt.age;
        this.con = function(){
            console.log('年龄为' +this.age + '岁姓名为' + this.name  
                        + '买了一辆排量为' + this.displacement + '的' + this.color
                        +'的' + this.brand + '车');
        }
    }
    var person = new Person({
        name: '王五',
        age: 22,
        carOpt:{
            displacement: '2.0T',
            color: '红色',
            brand: 'Benz'
        } 
    })
    console.log(person);
    person.con();
    

    在这里插入图片描述

  • 工作中多个人协同作业,最后合并成一个功能…

函数返回值
  • 普通函数默认返回undefined:return undefined
  • 构造函数被实例化以后默认返回this:return this
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值