【JS】原型与类的继承

  • 创建对象的方法:①字面量 ②通过构造函数 ③Object.create
  • 原型、构造函数、实例、原型链
  • instanceof的原理
  • new运算符
  • 继承的几种方式缺点

prototype属性:原型对象,默认对应这一个空对象,因此每一个prototype都是不相等的
显式原型和隐式原型

  • 每一个函数都有一个prototype属性,就是显式原型属性
  • 每一个实例对象都有一个__proto__,就是隐式原型
  • 实例对象的隐式原型等于对应的构造函数的显示原型的值
    在这里插入图片描述
    属性设置和屏蔽
var anotherObject = {
    a:2
}

var myObject = Object.create( anotherObject );//创建一个新对象myObject,并把这个新对象的Prototype属性关联到指定的anotherObject对象上

console.log(anotherObject.a);//2
console.log(myObject.a);//2,现在这个a存在myObject的原型链上
anotherObject.hasOwnProperty('a');//true
myObject.hasOwnProperty('a');//false

anotherObject.a++
console.log(myObject.a);//3

myObject.a++;//隐式屏蔽,先通过Prototype属性查找并获取属性a的值3,然后给这个值加1,用put方法将值4赋给myObject中新建的屏蔽属性a
console.log(anotherObject.a);//3
console.log(myObject.a);//4

总结:

  • 函数的prototype属性,在定义函数是自动添加的,默认值是一个空对象
  • 对象的__proto__属性,创建对象的时是自动添加的,默认值是构造函数的prototype属性:pensor.__proto__ === Person.prototype
  • 原型对象本身是一个普通对象,而普通对象的构造函数都是ObjectPerson.prototype.__proto__=Object.prototype
  • 所有的构造器都是函数对象,函数对象都是Function构造产生的:Object.__proto__===Function.prototype
  • Object的原型对象也有__proto__属性指向null,null是原型链的顶端:Object.prototype.__proto__==null
    总结:
  • 一切对象都是继承自Object对象,Object 对象直接继承根源对象null
  • 一切的函数对象(包括 Object 对象),都是继承自 Function 对象
  • Object 对象直接继承自 Function 对象
  • Function对象的__proto__会指向自己的原型对象,最终还是继承自Object对象

js中对原型的修改和重写
在这里插入图片描述

function Person(name) {
    this.name = name;
    this.age = 18;
    this.sayName = function() {
        console.log(this.name);
    }
}
// 第二步 创建实例
var person = new Person('person')

原型链

任何一个实例,通过原型链都可以找到它上面的原型,该原型对象中的方法和属性,可以被所有的原型实例共享

instanceof:用于判断实例属于哪个构造函数(原理是判断实例对象的__proto__属性和构造函数的prototype属性是否为同一个引用)

foo instance of Foo的结果为true,因为foo.__proto__===Foo.prototype是true
foo instance of Object的结果为true,因为Foo.prototype.__proto__===Object.prototype为true

constructor属性判断继承关系更为准确
例如:A继承了B,B继承了C,怎么判断a是由A直接生成的实例
答:因为a.__proto__.constructor===A为true,但是a.__proto__.constructor===Object的结果为false

参考文献:

类的定义、实例化

类的定义

  • 用构造函数模拟类(传统写法)
function Animal1(){
	this.name = 'lys';
}
  • 用class声明(ES6声明)
class Animal2{
	constructor(name){
		this.name = name
	}
}

实例化:不管用哪种方式定义,实例化都用new操作

继承

继承的本质是原型链。常见问题有继承的方式有几种,每种形式的优缺点是什么?

  • 方式1 借助构造函数:在子类构造函数中利用callapplybind修改this执行,将父类中的this指向子类,将父类实例属性挂载到子类实例上,实现继承
    function Parent1() {
        this.name = 'parent1 的属性';
    }

    function Child1() {
        Parent1.call(this);         //【重要】此处用 call 或 apply 都行:改变 this 的指向
        this.type = 'child1 的属性';
    }

    console.log(new Child1);

缺点:子类无法继承父类的原型

  • 方式2 通过原型链实现继承:将子类的原型(prototype)指向父类的实例对象
    /*
    通过原型链实现继承
     */
    function Parent() {
        this.name = 'Parent 的属性';
    }

    function Child() {
        this.type = 'Child 的属性';
    }

    Child.prototype = new Parent(); //【重要】

缺点:如果修改child1实例的对象属性,child2实例中的对象属性也会跟着改变,基本数据类型不会发生改变

  • 方式3 构造函数+原型链组合方式: 即可以继承父类原型的内容,也不会造成原型里属性的修改。缺点是让父亲的构造方法执行两次
 /*
    组合方式实现继承:构造函数、原型链
     */
    function Parent3() {
        this.name = 'Parent 的属性';
        this.arr = [1, 2, 3];
    }

    function Child3() {
        Parent3.call(this); //【重要1】执行 parent方法
        this.type = 'Child 的属性';
    }
    Child3.prototype = new Parent3(); //【重要2】第二次执行parent方法

    var child = new Child3();

参看链接:
面向对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值