javaScript是一种基于原型的面向对象的语言,而不是面向类的面向的对象语言
C++、Java有类Class和实例Instance的概念,类是一类事物的抽象,而实例则是类的实体
JS是基于原型的语言,它只有原型对象的概念,原型对象就是一个模板,新的对象从这个模板创建从而获取最初的属性。任何对象在运行时可以动态的增加属性,而且,任何一个对象都可以作为另一个对象的原型,这样后者就可以共享前者的属性
定义类
字面式声明方式
var obj={
property_1: value_1,
property_2: value_2
……
“property n”: value_n
}
var obj={
x:1,
1:'abc',
'y':'123'
}
for (let s in obj)
console.log(s,typeof(s))
这种方法也称作字面值创建对象
js 1.2开始支持
ES6之前–构造器
1、定义一个函数(构造器)对象,函数名首字母大写
2、使用this定义属性
3、使用new和构造器创建一个新对象
//定义类,构造器
function Point(x,y){
this.x=x
this.y=y
this.show=()=>{console.log(this,this.x,this.y)}
console.log('Point``````')
}
console.log(Point)
p1=new Point(4,5)
console.log(p1)
console.log('--------------')
// 继承
function Point3D(x,y,z){
Point.call(this,x,y)
this.z=z
console.log("Point3D~~~~~~~~~`")
}
console.log(Point3D)
p2=new Point3D(14,15,16)
console.log(p2)
p2.show()
new构建一个新的通用对象,new操作符会将新对象的this值传递给Point3D构造器函数,函数为这个对象创建z属性。
从上句话知道,new后得到一个对象,使用这个对象的this来调用构造器,那么如何执行基类的构造器方法,使用call方法,传入子类的this
ES6中的class
从ES6开始,新提供了class关键字,使得创建对象更加简单、清晰
1、类定义使用class关键字,创建的本质上还是函数,是一个特殊的函数
2、一个类只能拥有一个名为constructor的构造器方法,如果没有显式的定义一个构造方法,则会添加一个默认的constuctor方法
3、继承使用extends关键字
4、一个构造器可以使用super关键字来调用一个父类的构造函数
5、类没有私有属性
//基类定义
class Point{
constructor(x,y){
this.x=x
this.y=y
}
show(){
console.log(this,this.x,this.y)
}
}
let p1=new Point(10,11)
p1.show()
//继承
class Point3D extends Point{
constructor(x,y,z){
super(x,y)
this.z=z
}
}
let p2=new Point3D(20,21,22)
p2.show()
重写方法
//基类定义
class Point{
constructor(x,y){
this.x=x
this.y=y
}
show(){
console.log(this,this.x,this.y)
}
}
let p1=new Point(10,11)
p1.show()
//继承
class Point3D extends Point{
constructor(x,y,z){
super(x,y)
this.z=z
}
// 重写
show(){
console.log(this,this.x,this.y,this.z)
}
}
let p2=new Point3D(20,21,22)
p2.show()
子类中直接重写父类的方法即可
如果需要使用父类的方法,使用super.method()的方式调用
//基类定义
class Point{
constructor(x,y){
this.x=x
this.y=y
this.show=()=> console.log('Point')
}
// show(){
// console.log(this,this.x,this.y)
// }
}
let p1=new Point(10,11)
p1.show()
//继承
class Point3D extends Point{
constructor(x,y,z){
super(x,y)
this.z=z
// this.show=()=> console.log('Point3D')
}
show(){
console.log('Point3D')
}
}
let p2=new Point3D(20,21,22)
p2.show()
上例优先使用了父类的属性show
//基类定义
class Point{
constructor(x,y){
this.x=x
this.y=y
// this.show=()=> console.log('Point')
}
show(){
console.log(this,this.x,this.y)
}
}
let p1=new Point(10,11)
p1.show()
//继承
class Point3D extends Point{
constructor(x,y,z){
super(x,y)
this.z=z
this.show=()=> console.log('Point3D')
}
show(){
console.log('Point3D')
}
}
let p2=new Point3D(20,21,22)
p2.show()
优先使用了子类的属性
总结
父类、子类使用同一种方式类定义属性或者方法,子类覆盖父类。
访问同名属性或方法时,优先使用属性
静态属性
静态属性目前还没有得到很好的支持
静态方法
在方法名前加上static,就是静态方法了
class Add{
constructor(x,y){
this.x=x
this.y=y
}
static print(){
console.log(this.x)
}
}
add=new Add(40,50)
console.log(Add)
Add.print()
add.constructor.print()
静态方法中的this是Add类,而不是Add的实例
注意:
静态的概念和Python的静态不同,相当于Python中的类变量
this的坑
虽然js和C++、Java一样有this,但是Js的表现不同
原因在于,C++、Java是静态编译型语言,this是编译期绑定,而js是动态语言,运行期绑定
var school={
name:'String',
getNameFunc:function(){
console.log(this.name)
console.log(this)
return function(){
console.log(this===global)
return this.name
}
}
}
console.log(school.getNameFunc()())
函数执行时,会开启新的执行上下文环境ExecutionContext
创建this属性,但是this是什么就要看函数是怎么调用的了
1、myFunction(1,2,3),普通函数调用方式,this指向全局对象,全局对象是nodejs的global或者游览器中的window
2、myObject.myFunction(1,2,3),对象方法的调用方式,this指向包含该方法的对象
3、call和apply方法调用,要看第一参数是谁
分析上例
String和{ name: ‘String’, getNameFunc: [Function: getNameFunc] }很好理解
第三行打印的true,是console.log(this===global)执行的结果,说明当前是global,因为调用这个返回的函数是直接调用,这就是个普通函数调用,所有this是全局对象
第四行undefined,就是因为this是global,没有name属性
这就是函数调用的时候,调用方式不同,this对应的对象不同
显式传入
name='111'
var school={
name:'String',
getNameFunc:function(){
console.log(this.name)
console.log(this)
return function(that){
console.log(this===global)
return that.name
}
}
}
console.log(school.getNameFunc()(school))
通过主动传入对象,这样就避开了this的问题
ES3引入了apply、call方法
name='111'
var school={
name:'String',
getNameFunc:function(){
console.log(this.name)
console.log(this)
return function(){
console.log(this===global)
return this.name
}
}
}
console.log(school.getNameFunc().call(school))
apply、call方法都是函数本身的方法,第一参数都是传入对象引入的
apply传其他参数需要使用数组
call传其他参数需要使用可变参数收集
function Print(){
this.Print=function(x,y){
console.log(x+y)
}
}
p=new Print(1,2)
p.Print()
p.Print.call(p,10,20)
p.Print.apply(p,[10,20])
ES5引入了bind方法
name='111'
var school={
name:'String',
getNameFunc:function(){
console.log(this.name)
console.log(this)
return function(){
console.log(this===global)
return this.name
}
}
}
var fuc=school.getNameFunc()
console.log(fuc)
var boundfunc=fuc.bind(school)
console.log(boundfunc)
console.log(boundfunc())
apply、call方法,参数不同,调用时传入this
bind方法是为了函数先绑定this,调用时直接用
ES6引入支持this的箭头函数
var school={
name:'String',
getNameFunc:function(){
console.log(this.name)
console.log(this)
return ()=>{
console.log(this===global)
return this.name
}
}
}
console.log(school.getNameFunc()())
ES6新的定义方式
class school{
constructor(){
this.name='string'
}
getNameFunc(){
console.log(this.name)
console.log(this,typeof(this))
return ()=>{
console.log(this==global)
return this.name
}
}
}
console.log(new school().getNameFunc()())