1、类类是用于创建对象的模板。JavaScript 中生成对象实例的方法是通过构造函数,这跟主流面向对象语言(java ,C# )写法上差异较大,如下: function Point(x, y) { this.x = x; this.y = y;}Point.prototype.toString = function () { return '(' + this.x + ', ' + this.y + ')';};var p = new Point(1, 1); ES6 提供了更接近Java 语言的写法,引入了 Class(类) 这个概念,作为对象的模板。通过class关键字,可以定义类。
如下:constructor() 是构造方法,而this代表实例对象: class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; }} 类的数据类型就是函数,它本身就是指向函数的构造函数: // ES5 函数声明function Point() { //...}// ES6 类声明class Point { //.... constructor() { }}typeof Point // "function"Point === Point.prototype.constructor // true 在类里面定义的方法是挂到Point.prototype ,所以类只是提供了语法糖,本质还是原型链调用。 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; }}Point.prototype = { //.... toString()}var p = new Point(1, 1);p.toString() // (1,1) 类的另一种定义方式类表达式 // 未命名/匿名类let Point = class { constructor(x, y) { this.x = x; this.y = y; }};Point.name // Point 函数声明和类声明有个重要区别,函数声明会提升,类声明不会提升。 > let p = new Point(); // 被提升不会报错> function Point() {}> > let p = new Point(); // 报错,ReferenceError> class Point {}>
1.1 constructor()constructor() 方法是类的默认方法,new 生成实例对象时会自动调用该方法。
一个类必须有constructor() 方法,如果没有显式定义,引擎会默认添加一个空的constructor() 。 constructor() 方法默认返回实例对象(即this )。
class Point {}// 自动添加class Point { constructor() {}}
1.2 getter和setter与 ES5 一样,在类的内部可以使用get 和set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。 class User { constructor(name) { this.name = name; } get name() { return this.name; } set name(value) { this.name = value; }}
1.3 this类的方法内部的this ,它默认指向类的实例,在调用存在this的方法时,需要使用 obj.method() 方式,否则会报错。 class User { constructor(name) { this.name = name; } printName(){ console.log('Name is ' + this.name) }}const user = new User('jack')user.printName() // Name is jackconst { printName } = user;printName() // 报错 Cannot read properties of undefined (reading 'name') 如果要单独调用又不报错,一种方法可以在构造方法里调用bind(this) 。 class User { constructor(name) { this.name = name; this.printName = this.printName.bind(this); } printName(){ console.log('Name is ' + this.name) }}const user = new User('jack')const { printName } = user;printName() // Name is jack bind(this) 会创建一个新函数,并将传入的this作为该函数在调用时上下文指向。
另外可以使用箭头函数,因为箭头函数内部的this总是指向定义时所在的对象。 class User { constructor(name) { this.name = name; } printName = () => { console.log('Name is ' + this.name) }}const user = new User('jack')const { printName } = user;printName() // Name is jack
1.4 静态属性静态属性指的是类本身的属性,而不是定义在实例对象this 上的属性。 class User {}User.prop = 1;User.prop // 1
1.5 静态方法可以在类里面定义静态方法,该方法不会被对象实例继承,而是直接通过类来调用。 静态方法里使用this 是指向类。 class Utils { static printInfo() { this.info(); } static info() { console.log('hello'); }}Utils.printInfo() // hello 关于方法的调用范围限制,比如:私有公有,ES6 暂时没有提供,一般是通过约定,比如:在方法前面加下划线_print()表示私有方法。
2、继承Java 中通过extends 实现类的继承。ES6 中类也可以通过extends 实现继承。
继承时,子类必须在constructor 方法中调用super 方法,否则新建实例时会报错。 class Point3D extends Point { constructor(x, y, z) { super(x, y); // 调用父类的constructor(x, y) this.z = z; } toString() { return super.toString() + ' ' + this.z ; // 调用父类的toString() }} 父类的静态方法,也会被子类继承 class Parent { static info() { console.log('hello world'); }}class Child extends Parent {}Child.info() // hello world
2.1 super关键字在子类的构造函数必须执行一次super 函数,它代表了父类的构造函数。 class Parent {}class Child extends Parent { constructor() { super(); }} 在子类普通方法中通过super 调用父类的方法时,方法内部的this 指向当前的子类实例。 class Parent { constructor() { this.x = 1; this.y = 10 } printParent() { console.log(this.y); } print() { console.log(this.x); }}class Child extends Parent { constructor() { super(); this.x = 2; } m() { super.print(); }}let c = new Child();c.printParent() // 10c.m() // 2
2.2 _proto_和prototype初学JavaScript 时, _proto_ 和prototype 很容易混淆。首先我们知道每个JS 对象都会对应一个原型对象,并从原型对象继承属性和方法。 prototype 一些内置对象和函数的属性,它是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。 _proto_ 每个对象都有这个属性,一般指向对应的构造函数的prototype 属性。 下图是一些拥有prototype内置对象: 
根据上面描述,看下面代码 var obj = {} // 等同于 var obj = new Object()// obj.__proto__指向Object构造函数的prototypeobj.__proto__ === Object.prototype // true // obj.toString 调用方法从Object.prototype继承obj.toString === obj.__proto__.toString // true// 数组var arr = []arr.__proto__ === Array.prototype // true 对于function 对象,声明的每个function 同时拥有prototype 和__proto__ 属性,创建的对象属性__proto__ 指向函数prototype ,函数的__proto__ 又指向内置函数对象(Function)的prototype 。 function Foo(){}var f = new Foo();f.__proto__ === Foo.prototype // trueFoo.__proto__ === Function.prototype // true
2.3 继承中的__proto__类作为构造函数的语法糖,也会同时有prototype 属性和__proto__ 属性,因此同时存在两条继承链。 - 子类的
__proto__ 属性,表示构造函数的继承,总是指向父类。 - 子类prototype属性的
__proto__ 属性,表示方法的继承,总是指向父类的prototype 属性。 class Parent {}class Child extends Parent {}Child.__proto__ === Parent // trueChild.prototype.__proto__ === Parent.prototype // true
2.4 继承实例中的__proto__子类实例的__proto__ 属性,指向子类构造方法的prototype 。 子类实例的__proto__ 属性的__proto__ 属性,指向父类实例的__proto__ 属性。也就是说,子类的原型的原型,是父类的原型。 class Parent {}class Child extends Parent {}var p = new Parent();var c = new Child();c.__proto__ === p.__proto__ // falsec.__proto__ === Child.prototype // truec.__proto__.__proto__ === p.__proto__ // true
3、小结到此这篇关于前端JavaScript 中的class 类的文章就介绍到这了,更多相关JavaScript 中的class 内容请搜索51zixue.net以前的文章或继续浏览下面的相关文章希望大家以后多多支持51zixue.net! 下载地址: Vue实现大屏页面的屏幕自适应 javascript的基础交互详解 |