JavaScript继承

释放双眼,带上耳机,听听看~!

JS对象及继承方式综述

JS对象知识回顾

  • JS对象是若干无序属性的集合(数据属性、访问器属性、内部属性)

  • 生成对象的3种方式:字面量直接生成、Object工场方法、构造函数实例化对象

JavaScript继承

在上述的过程中,有一个Person.protorype.sayHi是给原型添加sayHi方法。

注意:create方法添加属性


1
2
3
4
5
6
7
8
9
10
1var empty = {};
2var obj2 = Object.create(empty, {
3    x: { value: 1 },
4    y: { value: 2, enumerable: true }
5});
6console.log(obj2);
7//返回的是自身的属性, 包括不可枚举属性, 但是会先返回可枚举属性, 之后返回不可枚举属性, 但是不可以返回继承的属性
8console.log(obj2.hasOwnProperty("x"));// true
9
10

其中的console.log(obj2),返回的是自身的属性,包括不可枚举属性,但是会先返回可枚举属性,之后返回不可枚举属性,但是不可以返回继承的属性。

JavaScript语言继承方式

  • JavaScript采用的是原型的继承方式,每个对象都有一个原型对象,最原始的原型是null

  • JavaScript的继承是对象-对象的原型继承,为面向对象提供了动态继承的功能

  • 任何方式创建的对象都有原型对象,可以通过对象的 __proto__属性来访问原型对象


1
2
3
4
5
6
7
8
9
10
11
12
13
1var obj = { num: 10 };
2console.log(obj.__proto__ === Object.prototype); // true
3var newObj = Object.create(obj);
4var newObj2 = Object.create(obj); //多个对象同一个原型的情况
5newObj.age = 23;
6console.log(newObj.__proto__ === obj); // true
7console.log(newObj2.__proto__ === obj); // true
8
9// 思考
10console.log(newObj.__proto__.__proto__); //Object.prototype
11console.log(newObj.__proto__.__proto__.__proto__); //null
12
13

上述的关系可以用以下图示来反映:

JavaScript继承

JS对象的原型链


1
2
3
4
5
6
7
8
9
10
11
12
1/*JS对象的原型链*/
2var proObj = { z: 3 };
3var obj = Object.create(proObj);
4obj.x = 1;
5obj.y = 2;
6console.log(obj.x); //1
7console.log(obj.y); //2
8console.log(obj.z); //3
9console.log("z" in obj); //true in可以从原型链上进行查找
10console.log(obj.hasOwnProperty("z")); //false hasOwnProperty不包括继承下来的属性
11
12

上述的代码可以变换成以下图示:

JavaScript继承


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1obj.z = 5;
2console.log(obj.hasOwnProperty("z")); // true
3console.log(obj.z); // 5
4console.log(proObj.z); ///3
5obj.z = 8;
6console.log(obj.z); //8
7delete obj.z; //true
8console.log(obj.z); //3
9delete obj.z; //true 此时会静默失败,由于此时obj本身没有z这个属性,因此将不会删除z
10console.log(obj.z); //still 3
11//如何删除原型上的属性
12delete obj.__proto__.z; //或者delete proObj.z;
13console.log(obj.z); //此时彻底没有z了
14
15

上述的代码可以变换成以下图示:

JavaScript继承

基于构造函数实现的原型继承

通过构造函数来创建对象

  • 当一个函数与new结合,该函数将作为构造函数来使用,用来创建JS对象

  • JS(ES5)中没有其他语言(C++、Java)中的类,JS中通过构造函数来实现类的功能

  • 在JS中构造函数也是对象,有一个重要的属性(原型 prototype),该属性与继承相关


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1/*基于构造函数实现的原型继承*/
2function Person(age, name) {
3    this.name = name;
4    this.age = age;
5}
6Person.prototype.sayHi = function() {
7    console.log("Hi,i'm " + this.name); // 此时的this指向的还是当前的对象即p1对象
8};
9var p1 = new Person(20, "Jack");
10console.log(p1.name);// Jack
11console.log(p1.age);// 20
12p1.sayHi();// Hi,i'm Jack
13
14

上述的代码可以转换为以下图示:

JavaScript继承

  • 构造函数有一个重要属性(原型prototype),该属性就是实例化出来的对象的原型

  • 构造函数的这个属性(原型 prototype)是真实对象,实例化的对象通过它实现属性继承

  • 可通过实例化出来的对象的__proto__属性来确认原型

  • 实例化的这个对象,有一个属性__proto__指向原型

  • 通过判断得知实例化出来的对象的__proto__就是构造函数的prototype属性

基于构造函数实现的原型继承以及原型链的图解

JavaScript继承

注意上述的constructor

constructor是定义在原型上的,可以通过p1的constructor属性来访问p1对象的构造器,但是实质上是p1的原型中的constructor属性。


1
2
3
4
5
6
7
8
9
1console.log(p1.__proto__ === Person.prototype); // true
2
3Person.prototype.constructor
4ƒ Person(age, name) {
5    this.name = name;
6    this.age = age;
7}
8
9

基于构造函数实现的原型继承-属性操作

JavaScript继承

JavaScript继承

JS对象-对象原型继承

JavaScript的原型继承是对象-对象的继承

  • 每个对象都有一个原型对象(可动态的指定原型,来改变继承关系,最原始的原型是null)

  • 思考并回答三种方式创建的对象的原型都是什么?

  • 多个对象继承于一个原型时,存在原型共享(节省内存如共享方法,但也带来了共享问题)


1
2
3
4
5
6
7
8
9
10
11
1//通过Object.create静态方法创建的对象的原型共享问题
2var superObj = {
3    x: 1,
4    y: 2
5};
6var subObj_First = Object.create(superObj);
7var subObj_Second = Object.create(superObj);
8subObj_First.__proto__.x = 5; //若此行写为subObj_First.x = 5;结果又是如何?
9console.log(subObj_Second.x);
10
11

上述代码中,通过Object静态方法创建了两个空对象,其中两个空对象的原型都是superObj,两个对象共享变量x和y,通过subObj_First.proto.x = 5;改变原型的x的属性的值,因此在subObj_Second.x访问x时,自身没有这个属性,会找到原型,输出5。

构造函数实现的对象-对象的原型继承的原型共享问题


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1function Person(name) {
2    this.name = name;
3}
4Person.prototype.age = 22;
5Person.prototype.showName = function() { console.log(this.name); };
6
7function Student(id) {
8    this.id = id;
9}
10//var p1 = new Person("Mike");Student.prototype = p1;
11Student.prototype = new Person("Mike");
12var s1 = new Student(2017001);
13var s2 = new Student(2017002);
14
15

上述的代码可以转化为下列图示:

JavaScript继承

上述代码的原型共享问题很严重,有弊端。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1//测试如下代码,思考为什么,这样的继承有什么弊端
2console.log(s1.name, s1.age, s1.id);
3console.log(s2.name, s2.age, s2.id);
4s1.__proto__.name = "Jack";
5console.log(s2.name);
6s2.__proto__.__proto__.age = 99;
7console.log(s2.age);
8/*
9输出结果:
10Mike 22 2017001
11Mike 22 2017002
12Jack
1399
14*/
15
16

两个对象的name都是从person里面的name得到的,显然对于一个学生来说应该只有一个name,并且age都是唯一的,但是该代码没有实现。如果给每一个对象都添加一个name和age,会造成内存的浪费,怎么解决的呢?

通过构造函数模拟类-类的继承

模拟类-类继承的形式一


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1//JS实现继承的形式 一
2function Person(name, age) {
3    this.name = name;
4    this.age = age;
5};
6Person.prototype.showName = function() { console.log(this.name); };
7
8function Student(name, age, id) {
9    Person.call(this, name, age);
10    this.id = id;
11}
12Student.prototype.__proto__ = Person.prototype;
13var s1 = new Student("xxx", 22, 2017001);
14var s2 = new Student("www", 23, 2017002);
15
16

上述的代码可以转化为如下图所示的内容:

JavaScript继承

思考:name属性添加到哪个对象上了?Person.prototype、Student.prototype还是实例化的对象上? Name添加到了实例化的对象上

推荐:将方法添加到对象的原型上(即构造函数的prototype上)便于共享,节省内存

模拟类-类继承的形式二


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1//JS实现继承的形式 二
2function Person(name, age) {
3    this.name = name;
4    this.age = age;
5};
6Person.prototype.showName = function() {
7    console.log(this.name);
8};
9
10function Student(name, age, id) {
11    Person.call(this, name, age);
12    this.id = id;
13}
14Student.prototype = Object.create(Person.prototype); // 形成继承关系
15// console.log(Person.prototype.constructor); //
16// console.log(Student.prototype.constructor); //
17Student.prototype.constructor = Student; // 把指飞的constructor指向正确的内容
18var s1 = new Student("xxx", 22, 2017001);
19var s2 = new Student("www", 23, 2017002);
20
21

思考:如果不把Student.prototype.constructor指回Student,那它将指向谁?

JavaScript继承

如果不指定那么Student.prototype.constructor就会指飞。

上述的代码可以转换为一下图示

JavaScript继承

JS继承补充部分

静态方法与原型方法的区别

  • 静态方法是构造器函数对象(类)的方法,原型方法是实例化对象(对象)的原型的方法

  • 使用形式有什么不同,区别在哪里?(属性共享)

  • 思考Object.getPrototypeOf(…)与Object.prototype.isPrototypeOf(…)


1
2
3
4
5
6
7
8
9
10
11
12
13
1//静态方法实例与原型方法实例
2var BaseClass = function() {};
3BaseClass.prototype.f2 = function() {
4    console.log("This is a prototype method ");
5};
6BaseClass.f1 = function() { //定义静态方法
7    console.log("This is a static method ");
8};
9BaseClass.f1(); //This is a static method
10var instance1 = new BaseClass();
11instance1.f2(); //This is a prototype method
12
13

再谈对象原型的constructor属性

  • 因为对象实例从原型中继承了constructor,所以可以通过constructor得到实例的构造函数

(1)确定对象的构造函数名


1
2
3
4
5
1function Foo() {}
2var f = new Foo();
3console.log(f.constructor.name);
4
5

(2)创建相似对象


1
2
3
4
5
6
7
8
9
1function Constr(name) {
2    this.name = name;
3}
4var x = new Constr("Jack");
5var y = new x.constructor("Mike");
6console.log(y);
7console.log(y instanceof Constr);
8
9

(3)constructor可用于指定构造函数


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1function Person(area) {
2    this.type = 'person';
3    this.area = area;
4}
5Person.prototype.sayArea = function() {
6    console.log(this.area);
7};
8var Father = function(age) {
9    this.age = age;
10};
11Father.prototype = new Person('Beijin');
12console.log(Person.prototype.constructor); //function person()
13console.log(Father.prototype.constructor); //function person()
14Father.prototype.constructor = Father; //修正constructor指向
15console.log(Father.prototype.constructor); //function father()
16var one = new Father(25);
17
18

对象的公有属性、私有属性(回顾闭包)

涉及到访问私有属性时,需将间接访问私有变量的函数定义在构造函数中


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1//公有属性、私有属性、特权方法
2function A(id) {
3    this.publicId = id;
4    var privateId = 456;
5    this.getId = function() {
6        console.log(this.publicId, privateId);
7    };
8}
9var a = new A(123);
10console.log(a.publicId);
11// console.log(a.privateId);
12a.getId();
13
14

在调用getId方法的时候,会访问到privateId,因此形成了闭包

给TA打赏
共{{data.count}}人
人已打赏
安全技术

C++ explicit关键字

2022-1-11 12:36:11

病毒疫情

守住疫情防控的最后一公里

2020-2-3 9:45:00

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索