42 class、继承、深/浅拷贝
42 class、继承、深/浅拷贝
一、class
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 );

浙公网安备 33010602011771号