原型链、继承、Class、this指向改变方法

原型

原型对象

  • 在JS中,每一个对象都会有一个原型对象,而函数也属于对象,故构造函数都有一个prototype属性,指向另外一个对象,该对象称为原型对象。
  • prototype属性指向一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
  • 可以把那些不变的方法,直接定义在prototype属性指向的原型对象上,这样所有的实例对象就可以共享这些方法了。
  • 原型对象的作用为了共享方法。

__proto__对象原型

  • 实例对象都会有一个属性__proto__指向构造函数的prototype原型对象。
  • __proto__对象原型===prototype原型对象。
  • __proto__对象原型的意义就在于为实例对象的查找机制提供一个方向或者一条路线。但是它是一个非标准属性,因此在开发中,不可以使用这个属性,它只是内部指向prototype原型对象。

constructor构造函数

  • __proto__对象原型或者prototype原型对象里面有一个属性constructor,该属性指向构造函数本身,故称之为constructor构造函数。
  • constructor构造函数主要用于记录该原型对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
  • 一般情况下,对象的方法都在构造函数的原型对象中设置。若有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个constructor指向原来的构造函数。但是如果需要往内置对象添加拓展方法时,不可以使用这种方法。

三者关系

原型三者关系.png

原型链

定义

JS的成员查找机制,当访问一个对象的属性和方法时,首先查找这个对象自身有没有该属性;若没有就查找它的原型对象(也就是__proto__属性指向的对象);若还没有就查找原型对象中__proto__属性指向的原型对象。以此类推一直找到null为止。
__proto__对象原型的意义就在于为对象成员查找机制提供一个方向。

原型对象this指向

构造函数中的this指向实例对象。
原型对象里面放的是方法,这个方法里面的this指向的是这个方法的调用者,也就是这个实例对象。

扩展内置对象

可以通过原型对象对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。
注意:数组和字符串内置对象不能给原型对象覆盖操作,不能是Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式。

原型链图示

原型链.png

Class

创建类

  • constructor构造函数

constructor方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new关键字创建对象实例时,会自动调用该方法。若没有显式定义,则类内部会自动给我们创建一个constructor

 class Person {
     constructor(name, age) { // constructor:构造器或者构造函数
         this.name = name;
         this.age = age;
     }
     // 也可以不用构造器
     static name = 'andy'; // 该name是静态属性
     readonly age = '18'; // 该age是只读属性
 }
  • 类中添加方法

方法之间不能加逗号分割,同时方法不需要添加function关键字

 class Person {
     constructor() {},
     say() {...} // say()是类中的方法
 }

创建实例

var xxx = new ClassName()
类必须使用new关键字。

类的继承

super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数(通过super.函数名调用)。

 class Person {
     constructor(surname) {
         this.surname = surname;
     }
 }
 class Student extends Person {
     constructor(surname, firstname) {
         // 调用父类的constructor(surname)
         super(surname); // 此处的super可以认为是Person父类的constructor
         // 定义子类独有的属性
         this.firstname = firstname;
     }
 }
  • 子类在构造函数中使用super,必须放在this前面(必须先调用父类的构造方法,再使用子类构造方法)。

  • 在ES6中类没有变量提升,故必须先定义类,才能通过类创建实例化对象。

  • 类里面的共有属性和方法一定要加this使用。

  • 一定要注意类里面的this指向问题,类的constructor()里面的this指向实例化对象,普通方法里面的this指向这个方法的调用者。

类的本质

  • class的本质还是function,类的所有方法都定义在类的prototype属性上。类创建的实例对象,里面也有__proto__指向类的
    prototype原型对象。故ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更加像面向对象编程的语法。

  • 故ES6的类其实就是语法糖。语法糖就是一种便捷写法。简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖。

继承实现

组合继承

ES6之前并没有提供extends继承,此时可以通过构造函数+原型对象模拟实现继承,故称为组合继承。

借用构造函数继承父类属性

核心原理:通过call()把父类的this指向子类型的this,这样就可以实现子类继承父类的属性。

 // 父类
 function Person(name, age, sex){
     this.name = name;
     this.age = age;
     this.sex = sex;
 }
 // 子类
 function Student(name, age, sex, score){
     // 此时父类的this指向子类的this,同时调用这个函数
     Peroson.call(this, name, age, sex);
 }

借用原型对象继承父类方法

一般情况下,对象的方法都在构造函数的原型对象中设置,通过构造函数无法继承父类方法。

 // 核心原理
 // 1. 将子类所共享的方法提取出来,让子类的prototype原型对象 = new 父类()
 // 2. 本质:子类原型对象等于父类的实例化对象,因为父类实例化之后另外开辟了内存空间,就不会影响原来父类的原型对象
 // 3. 将子类的constructor重新指向子类的构造函数

 // Student.prototype = Person.prototype; 这样赋值会有问题,若修改了子原型对象,父原型对象也会跟随一起变
 Student.prototype = new Person();

 // 若利用对象的形式修改了原型对象,别忘了利用constructor指回的构造函数
 Student.prototype.constructor = Student;

 // 这个是子构造函数专门的方法,实际上是new Person()这个地址的对象
 Student.prototype.exam = function(){
     console.log("学生要考试!")
 }

继承.png

使用组合继承实现示例

function Person(name, age){
    this.name = name
    this.age = age
}

function Student(name, age, score){
    Person.call(this, name, age)
    this.score = score
}

Student.prototype = new Person()
Student.prototype.constructor = Student

Person.prototype.say = function(){
    console.log('say')
}

Student.prototype.exam = function(){
    console.log('exam')
}

const student = new Student('老墨', 18, 100)

类继承

class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    say() {
        console.log('say')
    }
}

class Student extends Person {
    constructor(name, age, score) {
        super(name, age)
        this.score = score
    }
    exam() {
        console.log('exam')
    }
}

const student = new Student('老墨', 18, 100)

this指向改变方法

call方法

call方法接收的第一个参数就是this指向的目标对象,后面的参数都作为参数传入目标对象中,此方法只是临时改变this指向一次

function fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}

fn.call(obj, 1, 2); // this会变成传入的obj,传入的参数是参数序列
fn(1, 2) // this指向window

apply方法

apply方法接收的第一个参数就是this指向的目标对象,第二个参数是函数接收的参数,但必须是数组。此方法只是临时改变this指向一次

function fn(...args){
    console.log(this,args);
}
let obj = {
    myname: "张三"
}

fn.apply(obj, [1, 2]); // this会变成传入的obj,传入的参数必须是一个数组
fn(1, 2) // this指向window

bind方法

bind方法接收的第一个参数就是this指向的目标对象,后面传入的也是函数的参数序列(但是这个参数列表可以分多次传入)

改变this指向后,不会执行,返回一个永久改变this指向的函数

function fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}

const bindFn = fn.bind(obj); // this 也会变成传入的obj ,bind不是立即执行的,而是返回一个改变this指向后的函数
bindFn(1,2) // this指向obj
fn(1,2) // this指向window

小结

  • 三者都可以改变函数的this指向。第一个参数都是this指向的目标对象,若第一个参数忽略或为undefined或为null,其函数的this指向都是window
  • 三者都可以传参,其中apply必须传入一个数组作为参数,call则传入一个参数序列,bind则可以多次传入参数
  • bind不会执行函数,而是返回一个改变this指向的函数,返回的函数是永久性改变this的;applycall会立即执行函数,改变的this指向只是临时的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员墨~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值