42 class、继承、深/浅拷贝

 

42 class、继承、深/浅拷贝

一、class

1.面向对象编程

1.1 概念

面向对象是一种以对象为中心的编程思想。面向对象是相对于面向过程来讲的,面向对象把相关的数据和方法组织为一个整体来看待,从更高的层次来进行系统建模。

面向过程

面向过程思想强调的是步骤,当碰见问题时,思考的是“我该怎么做”,分析出解决问题所需要的步骤,一步步的去实现。 例如:想吃蛋炒饭,首先想到的是我要如何做,包括哪些步骤。比如:起锅烧油,加入鸡蛋,加入米饭,加入调料,翻炒,装盘等。

面向对象

面向对象思想强调的是对象,当碰见问题时,思考的是“我该让谁来做”。这个“谁”其实就是对象。找合适的对象做合适的事情。而对象如何去做(采用什么样的步骤)我们就不关心了,最终把问题解决掉就可以了。

例如:还是想吃蛋炒饭,首先想到的是让谁来帮我做蛋炒饭,比如找厨师来帮我做蛋炒饭。具体厨师如何去做这个蛋炒饭,做饭的步骤是怎么样的我们并不关心。只要最终把蛋炒饭做好就可以了。

1.2 类

ES5中创建对象用的是function:构造函数。但是其它编程语言觉得比较另类 。

类来说明一类事物(有相同的特性)

程序中先有类后有对象 。由类去实例化对象。模子:没有具体的功能。有功能:功能是实例化对象 。

1.3 对象

对象才是实实在在的物体 。我平时要使用某个功能就是找的对象 。

2.class的基本使用

功能就是描述类的。

代码示例:

//语法
class ClassName{
...
    定义属性和方法
}

 

对比ES5和ES6实现对象/类:

ES5的functioin代码案例:

ES5:问题:没有整体
function Person(name,age){
this.name = name  //初始化属性
this.age = age
}

Person.prototype = {  //原型上添加方法
say(){
console.log( `${this.name}在说话` )
}
}
new Person( '小黄',20 )

ES6的Class

//1. 实例化对象以后添加属性。
// class Person{
       
// }

// let p1 = new Person //不传参,可以不用写括号

// console.log( p1 );
// p1.name = '小明'
// p1.age = 30

//2. 有些类默认就有一些属性
// class Person{
//     name = '小明'
//     age = 12
// }

// let p2 = new Person
// console.log( p2 );

 

3.constructor

class Person{

   constructor( age,name ){ //构造函数,不能手动调用 。每次实例化都会调用一次
       //经常做一些初始化的工作 ,比如:初始化对象的属性
       // console.log( '我执行了构造函数' );
       // console.log(age,name);
       console.log(this);
       this.age = age
       this.name = name
  }
}

// let p1 = new Person
// let p2 = new Person

let p3 = new Person( 18,'付老师' )
console.log( p3 );
 class Person {
       constructor(name, age, address) {
           this.age = age
           this.name = name
           this.address = address

      }

       // sleep=()=>{ //可以写箭头函数。但是不建议。
       //     console.log(this);
       // }
       sleep(){
           console.log( this.name + '在睡觉' );
      }
}

   let p1 = new Person('小明',20,'北京朝阳')

   console.log( p1 );

 

4.静态、成员(实例)属性/方法

成员属性/方法:对象上的属性,私有的。比如人:身高、体重 、身份证号 ...。比如小明在路上捡了100元。那么这100元就属于小明私有的 。

静态属性/方法:公共的。不属于对象,是属于类的。比如:学生的班级名称。

什么时候用?和对象本身没有关系,就可以用静态的。

代码示例:

成员属性:
class Person{
name
sex
}

let p1 = new Person
p1.money = 100

let p2 = new Person

 

静态属性和方法

static
//语法
class Person{
static staticProps = value
static staticMethod(){

}
}
//------------------------------------
class Person{
   static A = '我是静态属性A'
   
   static m1(){
       console.log( '我是静态方法m1' )
  }
}

let p1 = new Person

console.log( p1.A ,'1111');
console.log( Person.A ,'2222')

Person.m1()

原生js中哪些静态方法?

  • Promise.all

  • Object.keys

  • Object.values

  • Object.assign

  • Array.of()

  • Array.from()

  • Date.now()

使用技巧

class Student{
static CLASSNAME = 'web1208'
constructor( username,idcard ){
this.username = username
this.idcard = idcard
//this.classname = 'Web1207'
}

//往对象上的原型上绑定一个方法,向外暴露你想使用静态属性
getClassName(){
return Student.CLASSNAME
}

getClassName2(){
// console.log( this.__proto__.constructor === Student )//true 指向当前的类
//return Student.CLASSNAME
return this.__proto__.constructor.CLASSNAME
}
}

let stu1 = new Student()
console.log( stu1.getClassName() )
let stu2 = new Student()
console.log( stu2.getClassName() )
let stu3 = new Student()
console.log( stu3.getClassName() )


let stu = new Student()
console.log( stu.getClassName2() )

 

二、继承

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

1.基本用法

基本的语法:

作用:继承父类所有的特性。在程序中还能实现减少代码量的过程 。

class Son extends Father{

}

代码案例:

class Person{
name = 'aaa'
age = 20

say(){
console.log( this.name + '在说话' )
}
}

class Student extends Person{

}

let stu = new Student
stu.say()

 

2.super的用法

super: 是在子类的constructor中的第1行进行调用的。而且子类有构造函数的时候,必须调用 。

代码案例1:


class Person{
constructor( name,age ){
this.name = name
this.age = age
}

say(){
console.log( this.name + '在说话' );
}
}

class Student extends Person{ //继承了父类所有的信息


sing(){
console.log( this.name + '花了10w' + '会唱歌了' );
}
}

let stu = new Student( '小明',20 ) //
stu.say()
stu.sing()
console.log( stu );

let p1 = new Person( '小芳',18 )
console.log( p1.sing );//undefined
p1.sing()//sing is not a function

 

代码示例2:

 class Person{

static A = '我是父类的静态A属性'
constructor( name,age ){
this.name = name
this.age = age
//console.log('我是父类的方法,我先用了')
}

say(){
console.log( this.name + '在说话' );
}
}

class Student extends Person{
constructor( idcard,name,age ){
super( name,age ) // 如果子类有constructor,那么一定要在构造函数内的第一行调用
//就是执行父类的constructor。
//console.log( '我是子类的constructor' );
this.idcard = idcard
}
}
let stu = new Student( 'a0001','小明',20 )
// console.log( stu );
console.log( Student.A )

 

3.面向对象的应用场景

3.1 封装公共方法

分析原生js的实现原理。

代码案例:

let str = 'abcd'
str.length
str.substring()

//Number、String、Array、Object
class Js {

String(){
return {
substring(){

},
indexOf(){

},
charAt(){

},
toUpperCase(){

}
}
}
Array(){
return {
push(){
console.log( '我是array的push方法' );
},
pop(){
console.log( '我是array的pop方法' );

}
}
}
}


let T = new Js()

let arr = T.Array()
arr.push()
arr.pop()

 

3.2 框架中使用

你只要会用。包括99%的框架基本都是面向对象思想封装的。有的框架是function 、有的是class

 // console.log( $ );

$.prototype.aaa = '12341234'

let jq = $() //返回的是jquery实例对象
// console.log( jq.aaa )
// console.log( $.fn );
console.log( jq.addClass );

// $.fn = function(){
// console.log('静态方法')
// }
// $.fn11()

 

三、深/浅拷贝

1.浅拷贝

只拷贝一层的是浅拷贝 。

...
Object.assign
for...in

let obj = {
username:'zbj',
hobbies:['吃','喝','背翠兰']
}

let newObj = {}

for( let attr in obj ){
newObj[attr] = obj[attr]
/*
newObj.username = obj.username
newObj.hobbies = obj.hobbies
*/
}

newObj.hobbies.push( '睡觉' )
console.log( obj );

 

2.深拷贝的实现

浅拷贝+递归

难点:Array和Object的判断 。

判断数据类型的别一种高级方式

Object.prototype.toString.call( 数据 )
let num = 234
let arr = [11,22,33]
let obj = {}
var fn = function(){}
// console.log( num.toString() )//123
// console.log( arr.toString() )//'11,22,33'
// console.log( arr );

console.log( Object.prototype.toString.call( num ) )// [object Number]
console.log( Object.prototype.toString.call( arr ) )// [object Array]
console.log( Object.prototype.toString.call( obj ) )// [object Object]
console.log( Object.prototype.toString.call( fn ) )// [object Function]

代码案例:


// 浅拷贝+ 递归 + 判断数组和对象
let obj1 = {
username: 'zbj',
hobbies: ['吃', '喝', '背翠兰']
}

//let obj = ['吃','喝','背翠兰',[111,22,33]]

//let obj = 123
//判断obj的数据类型Array还是Object 其它的类型直接返回

function deepCopy(obj) {
let newObj

let type = Object.prototype.toString.call(obj)
if (type === '[object Object]') { //是对象
newObj = {}
} else if (type === '[object Array]') { //是数组
newObj = []
} else {
// newObj = obj //基本类型
return obj
}

for (let attr in obj) {
//如果遍历出来的每一个值 是基本类型则直接赋值。如果是引用类型,需要重新调用一遍
// console.log( attr );
if( typeof obj[attr] == 'object' ){
newObj[attr] = deepCopy( obj[attr] ) //这里是引用类型
}else{
newObj[attr] = obj[attr]//这是基本类型
}


}

// console.log(newObj);
return newObj
}

let o = deepCopy( obj1 )
// console.log( o );
o.hobbies.push( 11111 )
console.log( o,'ooooo' );
console.log( obj1 );

 

3.补充

JSON.parse / JSON.stringify

可以实现深拷贝。但是有一个问题:源数据中如果有函数会丢失 。

如果源数据中没有函数,则可以大胆使用。

JSON.stringify( object ) //把对象转换成json字符串

JSON.parse( jsonstr ) //把json字符串转换成js对象

JSON字符串比如双引号。

代码示例:


let obj = {}
let newObj = JSON.parse( JSON.stringify( obj ) )

let obj1 = {
username: 'zbj',
hobbies: ['吃', '喝', '背翠兰'],
fn:function(){

}
}

console.log( JSON.stringify( obj1 ) );//对象转换成字符串的时候,函数就消失了。


// let newObj = JSON.parse( JSON.stringify( obj1 ) )

// newObj.hobbies.push( 3333 )
// console.log( newObj );
// console.log( obj1 );

 

 

posted @ 2021-05-07 22:19  一花一世界111  阅读(107)  评论(0)    收藏  举报