JS类—class


典型的面向对象编程语言(比如C++和Java)存在类(class)这个概念。所谓类就是对象的模板,对象是类的实例

JS中没有类,在ES5中使用构造函数(constructor)作为对象的模板。但是ES5中实现继承的代码非常冗长与混乱(盗用构造函数、组合继承、原型式继承等等),因此在ES6中新引入了class关键字具有了正式定义类的能力

  类(class)是ECMAScript中新的基础性语法糖结构。其背后使用的仍然是原型和构造函数的概念

1.类定义

class Person {}
const Person = class {}

 说明:

  • class声明不存在变量提升:
    • console.log(classDemo);
      class classDemo {};        // var_let_const.html:12 Uncaught ReferenceError: Cannot access 'classDemo' before initialization
  • 函数受函数作用域限制,而类受块作用域限制:
    •         {
                  class classDemo { };
              }
      
              console.log(classDemo);        // var_let_const.html:16 Uncaught ReferenceError: classDemo is not defined
  • 类名首字母同函数构造函数一样,建议类名首字母大写

2.类构造函数

constructor关键字用于在类定义块内部创建类的构造函数。方法名constructor会告诉解释器在使用new操作符创建类的新实例时,应该调用这个函数。

构造函数非必须,不定义相当于构造函数为空

示例:

        class Person { 
            constructor(override){
                console.log("我是一个人")
            }
        };

        let p1 = new Person()        // 我是一个人

 (1)使用new实例化类

  使用new实例化的操作等于使用new调用其构造函数,new执行的操作有:

  • 在内存中创建一个新对象
  • 这个新对象内部的__proto__属性赋值为构造函数的prototype属性
  • 构造函数内部的this被赋值为这个新对象(this指向了新对象)
  • 执行构造函数内部的代码
  • 如果构造函数返回非空对象,则返回该对象,否则返回刚创建的新对象

说明:

  • 类实例化时传入的参数会用作构造函数的参数。如果不需要参数,则类名后面的括号是可选的
  • 类构造函数与构造函数的主要区别在于:
    • 调用类构造函数必须使用new操作符。不使用new则会抛出错误(Uncaught TypeError: Class constructor Person cannot be invoked without 'new'
    • 而普通构造函数如果不使用new调用,那么就会以全局的this(window)作为内部对象

(2)把类当成特殊函数

JS中并没有正式的类这个类型。从各方面看,ECMAScript的类就是一种特殊的函数。可以使用typeof 检测表明它是一个函数:

        class Person { };

        console.log(typeof Person);    // function
  • 可以使用instanceof操作符检查一个对象是不是类的实例:
1         class Person {};
2 
3         let p = new Person()
4 
5         console.log(p instanceof Person);    // true
  • 类也可以向其他对象或者函数引用一样把类作为参数传递
 1         let pList = [
 2             class Person {
 3                 constructor ( id ){
 4                     this._id = id
 5                     console.log("类作为参数:",this._id)
 6                 }
 7             }
 8         ]
 9 
10         function createIns( classDefinition, id ){
11             return new classDefinition(id)
12         }
13 
14         let foo = createIns(pList[0],1234)    // 类作为参数: 1234
  •  类还可以立即实例化
1         let foo = new class Person {
2             constructor(id) {
3                 console.log("立即实例化对象")
4             }
5         }

3.实例、原型和类成员

每次通过new调用类标识符时,都会执行类构造函数,在这个函数内部,可以为新创建的实例(this)添加自有属性。且构造函数执行完毕以后,仍然可以给实例继续添加新成员。

(1)实例成员

每个实例都对应一个唯一的成员对象,即所有成员都不会在原型上共享:

 1         class Person {
 2             constructor(id) {
 3                 this.name = new String('Jack')
 4 
 5                 this.sayName = () => console.log(this.name);
 6 
 7                 this.nickNames = ['张三','李四']
 8             }
 9         }
10 
11         let p1 = new Person(),p2 = new Person();
12 
13         p1.sayName();    // Jack
14         p2.sayName();    // Jack
15         console.log(p1.name === p2.name); // false
16         console.log(p1.sayName === p2.sayName); // false
17         console.log(p1.nickNames === p2.nickNames); // false

(2)原型方法与访问器

为了在实例间共享方法,类定义语法把在类块中定义的方法作为原型方法

 1         class Person {
 2             constructor() {
 3                 // 存在这个类的不同的实例上
 4                 this.locate = () => console.log("instance");
 5             }
 6             // 在类块中定义的所有内容都会定义在类的原型上
 7             locate() {
 8                 console.log("prototype");
 9             }
10         }
11 
12         let p = new Person()
13 
14         p.locate()    // instance
15         Person.prototype.locate()    // prototype
  • 可以把方法定义在类构造函数中或者类块中,但不能在类块中给原型添加原始值或对象作为成员数据
1         class Person {
2             name: '张三'
3         }
4         // Uncaught SyntaxError: Unexpected identifier
  • 类方法等同于对象属性,因此可以使用字符串、符号或计算的值作为键
 1         let funName = "fn02"
 2 
 3         class Person {
 4             
 5             fn01(){
 6                 console.log("字符串属性名");
 7             }
 8 
 9             [funName](){
10                 console.log("变量属性名");
11             }
12 
13             ['fn' + '03'](){
14                 console.log("计算属性名");
15             }
16 
17         }
18         
19         let p = new Person()
20         p.fn01()    // 字符串属性名
21         p.fn02()    // 变量属性名
22         p.fn03()    // 计算属性名
  • 类定义也支持get与set访问器:
 1         class Person {
 2 
 3             set name(newName){
 4                 console.log("设置新值为:",newName);
 5                 this._name = newName
 6             }
 7             
 8             get name(){
 9                 console.log("读取到新值:",this._name);
10                 return this._name
11             }
12 
13         }
14         
15         let p = new Person()
16         p.name = "张三"        // 设置新值为: 张三
17         p.name    // 读取到新值: 张三

 (3)非函数原型和类成员

类定义并不显式支持在原型或类上添加成员数据,但是可以在类定义外部手动添加:

 1         class Person {
 2 
 3             sayName() {
 4                 console.log(`${ Person.greeting }${ this.name }`);
 5             }
 6 
 7         }
 8         // 在类上定义数据成员
 9         Person.greeting = ' 我的名字是'
10         // 在原型上定义数据成员
11         Person.prototype.name = '张三'
12         
13         let p = new Person()
14         p.sayName()    // 我的名字是张三

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM