前端入门14-JavaScript进阶之继承

《JavaScript权威指南》

MDN web docs

Github:smyhvae/web

作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。

PS:梳理的内容以《JavaScript权威指南》这本书中的内容为主,因此接下去跟 JavaScript 语法相关的系列文章基本只介绍 ES5 标准规范的内容、ES6 等这系列梳理完再单独来讲讲。

正文-继承

继承是面向对象编程语言中一大特性,Java 中的继承是静态的,通过在编写 class 代码过程中指定,一旦继承关系确定了,就无法在运行期间去修改了。

子类默认继承父类的所有非私有的属性和方法。

但在 JavaScript 中,由于并不存在类的机制,而且它是动态的基于原型的继承,所以在很多方面与 Java 的继承并不一样。

下面从多个方面来进行比较:

用法 //Java class MyTask extends Thread {} //JavaScript var a = Object.create({b:1});//Object.create方式指定继承的对象 function A() {} A.prototype.b = 2; //构造函数的prototype方式指定继承的对象 var a = new A();

在 Java 中只能通过 extends 关键字声明继承的关系。

在 JavaScript 中有两种方式指定继承的原型对象,一种用 Object.create(),一种通过构造函数的 prototype 属性。

当在声明一个自定义的构造函数时,内部会自动创建一个空的对象(new Object()),然后赋值给构造函数的 prototype 属性,之后通过该构造函数创建的对象,就都默认继承自 prototype 指向的空对象,所以可在这个空对象上直接动态的添加属性,以便让创建的对象都可以继承这些属性。

继承的内容

Java

在 Java 中,存在:类,实例对象两种概念。

因此,也就有了类属性、类方法、对象属性、对象方法的说法,这些的区别在于是否有 static 关键字声明。

public class Animal { public int age; //对象属性 public void eat(){}//对象方法 public static void dead(){}//类方法 } public class Dog extends Animal { public void growUp(){ eat();//子类可直接使用父类的非私有方法 dead();//包括类方法 } } //使用 Dog dog = new Dog(); dog.age = 15; //对象属性和方法需通过实例对象才可进行操作 dog.eat(); dog.dead();//类属性和类方法不实例化对象也可使用,通过对象也可使用 Dog.dead();

对象属性和对象方法必须经过类的实例化操作,创建出一个对象来时,才可以通过对象操作这些属性和方法。

而类属性和类方法在子类中可以直接使用,子类实例化的对象也可直接调用。

JavaScript

在 JavaScript 中只有对象的概念,被继承的对象称为原型。

function Animal() {} Animal.prototype.age = 0; //为原型添加属性 Animal.prototype.eat = function () {console.log("eat")} function Dog() {} //Dog构造函数的prototype继承自Animal.prototype Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; //由于手动修改了原型链,破坏了默认的三者关联,手动修补 Dog.prototype.growUp = function () {console.log("growUp")} //dog 对象的原型链:dog -> Dog.prototype -> Animal.prototype -> Object.prototype -> null var dog = new Dog(); dog.age; dog.eat(); dog.growUp();

先定义个 Animal 构造函数,然后注意,JavaScript 是基于原型继承的,此时如果要定义一些可继承的属性,需要在 Animal.prototype 对象上添加,不可在构造函数本身上添加。

然后再定义一个 Dog 构造函数,让它继承自 Animal.prototype,注意,因为在这里手动修改了原型链,所以最好手动补上 Dog.prototype.constructor = Dog 这行代码,让构造函数、实例对象、原型三者间仍旧可以保持默认的关联关系。

最后,通过构造函数 Dog 创建的对象,就可使用继承而来的属性。

还有另一种写法:

function Animal() { this.age = 0; this.eat = function () {console.log("eat")} } function Dog() {} Dog.prototype = Object.create(new Animal());

可以直接在构造函数 Animal 中添加相关属性,但涉及要继承时,需要使用 new Animal() 作为原型参数,如果直接使用 Animal,那么将会误认将函数对象本身作为原型。

不过这种方式,需要注意,当涉及多个对象需要继承自同一个原型时,原型对象的实例应该只有一个,这样才能保证对原型对象动态修改的属性能同步到所有继承的子对象上。

权限控制

Java 中有权限修饰符,子类可以使用父类中非私有的属性和方法。

但在 JavaScript 中,没有公有、私有权限之说,所有定义在原型中的属性,子对象中都可以使用。但可以利用对象属性的特性,在原型中控制它的属性的可枚举性、可配置性、可写性,以此来达到控制子对象访问原型属性的一些限制。

修改对象属性的特性用:Object.defineProperty()

同理,对象本身也有一些特性可利用,比如 Object.freeze(),Object.seal() 这类方法可以限制对原型对象进行扩展等操作。

动态同步

Java 中,每个从类实例化出来的对象之间都是相互独立的,不会相互影响,而类属性,类方法只是它们可以用来共享、通信的渠道而已。

而且,类机制是静态的,在Java中,并不会存在在运行期,修改类相关属性而影响子类的场景。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxwsw.html