ES6类的理解

类和构造函数的区别

ES5中函数定义类(构造函数):

function Foo(x,y) {
    this.x = x;
    this.y = y;
}

Foo.prototype.toString = function(){
	console.log(this.x, this.y)  
}

var foo = new Foo(1,2)
foo.toString()  

// 1 2

ES6中定义类:

调用类的方法也就是调用原型(prototype)上的方法

class Foo {   
    constructor(x,y){
     this.x = x;
     this.y = y;
    }
    
    toString(){
     console.log(this.x, this.y)
    }
}

var foo = new Foo(1,2)
foo.toString()  

// 1 2

ES6中,class实际上也是一个function对象,其原型链与es6一致,但有几点需要注意

  • 类里的constructor()方法表示构造函数,与原型链中的.constructor无关
  • 在class中声明的函数会直接添加到原型中,可以直接使用,不需要经过原型
  • 通过new关键字生成实例时,只会执行constructor构造函数里的内容
class Foo {   
    constructor(x,y){
     this.x = x;
     this.y = y;
    }
    
    toString(){
     console.log(this.x, this.y)
    }

    
}

Foo.prototype.name = 'Jack'
var foo = new Foo(1,2)

console.log(Foo)                            // Function: Foo]
console.log(foo)                            // Foo { x: 1, y: 2 }

console.log(Foo === foo.constructor)        // true    成员访问对象     foo.__proto__.constructor

console.log(Foo.prototype)                  // Foo { name: 'Jack' }     对象访问原型
console.log(foo.__proto__)                  // Foo { name: 'Jack' }     成员访问原型

es5的构造函数和静态方法

function Person(name, age) {
    // 构造函数里的方法和属性
    this.name = name
    this.age = age 
    this.run = function() {
        console.log(`${this.name}, ${this.age}`)
    }
}

// 设置原型属性
Person.prototype.sex = '男'
Person.prototype.work = function() {
    console.log('work')
}

// 设置静态方法
Person.setName = function() {
    console.log('静态方法')
}

var person = new Person('张三', '23')
person.run()    // 实例共享原型的属性
person.work()


Person.setName()    // 用构造函数来执行静态方法

ES6里的类和静态方法

类里直接声明的函数实际上是在原型中进行声明,如果要特地指明静态方法,需要用static关键字,这样实际上就是Person.xxx直接添加类的一个属性

class Person {
    
    say() {
        console.log('实例方法')
    }
    
    static work() {
       	console.log('静态方法')
    }
}

const person = new Person()
person.say()	// 实例内部找不到该方法时,可以通过原型找到该方法并调用
Person.work()	// 静态方法相当于类的一个属性,因此可以通过类来直接调用

类的继承
  • 类中直接定义的方法,实际上是在原型中定义,但是它是隐式的
class Demo {
	classFun() {
        
    }   
}
console.log(Demo.prototype)				// Demo {}
console.log(Demo.prototype.classFun)	// [Function: classFun] 实际上是有的
Demo.prototype.other = 'other'			// 直接通过原型声明的话会显示
console.log(Demo.prototype)				// Demo { other: 'other' }
  • 使用extentds关键字实现继承时,实例化子类时,会执行父类的构造函数constructor()方法
class Father {
    constructor() {
		console.log('执行父类构造函数')
		this.fatherAttribute = '这里是父类的属性'
	}
}

class Child extends Father{

	
}

const child = new Child()	// 打印:'执行父类构造函数'
// 相当于视实例child去调用了父类的构造函数,this指向child,因此会在child创建 fatherAttribute
console.log(child)		// Child { fatherAttribute: '这里是父类的属性' }
  • 在一个使用extentds关键字实现继承的类中,如果不声明constructor()方法,在实例化子类时会默认调用父类的构造方法,但是如果声明了constructor(),必须要手动去调用父类构造函数(super()),否则子类constructor()构造函数会报错
class Father {
    constructor() {
		console.log('执行父类构造函数')
		this.fatherAttribute = '这里是父类的属性'
	}
}

class Child extends Father{
	constructor() {
		super()		// super()表示调用父类的构造函数,声明构造函数而不调用super()会报错
	}	
}
  • 可以在子类构造函数里使用super.xxx去调用父类在原型中声明的方法
class Father {
    constructor() {
		console.log('执行父类构造函数')
		this.fatherAttribute = '这里是父类的属性'
	}

	implicitFunction() {
		console.log('调用父类原型中的隐式方法')
	}
}

Father.prototype.explicitFunction = () =>{
	console.log('调用父类原型中的显式方法')
}

class Child extends Father{
	constructor() {
		super()					 
		super.implicitFunction()	 // 调用父类原型中的隐式方法
		super.explicitFunction()	 // 调用父类原型中的显式方法
	}	
}
  • 继承在原型链中的关系

    和ES5原型链继承有所不同,类(类也是构造函数)在继承中,子类构造函数的原型不是父类的实例,且__proto__指向父类构造函数而非Function的原型

    父类构造函数的__proto__仍指向Function原型

class Father {
	constructor(){		// 注明:如果父类中没有声明构造函数,则无法成功被继承

	}
}

class Child extends Father{

}

Child.prototype.pro = '子类原型属性'

const child = new Child()	

console.log(child.constructor == Child)							// true
console.log(child.__proto__.constructor == Child)				// ture					
console.log(child.__proto__.__proto__ == Father.prototype)		// true
console.log(child.constructor.__proto__ == Father)				// ture
console.log(child.__proto__.__proto__.constructor == Father)	// true
console.log(Child.__proto__ ==  Father)							// true
console.log(Child.prototype)									// Child { pro: '子类原型属性' }

ES5使用原型链继承中,子类和父类构造函数的__proto__都是指向Function原型的

function Father() {
	this.num = 1
    this.obj = {value:1}
}
function Child() {
	Father.call(this)			
}

Child.prototype = new Father()
const child = new Child()				
	
Function.prototype.app = 'app'

console.log(Child.__proto__)		// { [Function] app: 'app' }
console.log(Father.__proto__)		// { [Function] app: 'app' }

类的单例设计

一个类可以生成多个实例,每次new的实例都是互不干扰的,单例就是让类生成一个固定的实例

class Person {
    
    constructor() {
        this.name = 'lucy'
    }
    
    // 一个静态方法,用于生成一个单例
    static getInstance() {
        if(!Person.instance){   // 如果尚未创建实例,则进行创建,如果存在,则一直返回该实例
            Person.instance = new Person()
        }
        return Person.instance
    }
}
const person = Person.getInstance()		// 注意,这里既然想用一个单例,则就不用new关键字去生成实例了,如果直接使用new还是会生成互不干扰的各个实例

console.log(person.name)	    // lucy
person.name = 'jack'          // 对实例属性进行修改

const person_ = Person.getInstance()
console.log(person_.name)     // jack,可以证明用该方法生成的实例,会返回同一个实例

const personA = new Person()  // 使用new关键字可以创建其他实例,实例间互不干扰
console.log(personA.name)     // lucy
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值