继承和深拷贝封装
1.es5寄生组合式继承
2.es6类的继承
3.深拷贝函数封装
01-es5寄生组合式继承
# 属性的继承
`方法`: 在子构造函数内部通过call方法调用父构造函数:
`代码`: function Son() {
Father.call(this, 属性)
}
`原理`:子构造函数内部的this会指向s这个实例对象,通过在子构造函数内用call方法调用父构造函数,将父构造函数内部的this指向强制改变成了s这个实力对象,原本挂载到父构造函数身上的属性,自然就挂载到了s实例对象上了
# 方法的继承
`方法`:强制将子构造函数的原型对象的隐式原型指向父构造函数的原型对象
`代码`: Son.prototype = Object.create(Father.prototype)
`原理`:强制将子构造函数的原型对象的隐式原型指向父构造函数的原型对象,通过原型链关系图,子构造函数的实例对象就可以使用父构造函数原型对象上的方法
`Object.create(obj)`: 拷贝一个新对象,新对象的隐式原型会指向源对象
# 手动将子构造函数的原型对象的构造器指回子构造函数本身
`代码`: Son.prototype.constructor = Son
完整代码
function Father(chick, duck, sheep) {
this.chick = chick
this.duck = duck
this.sheep = sheep
}
Father.prototype.farm = function () {
console.log('种田');
}
function Son(chick, duck, sheep, pig) {
// 注意:先继承再自有
// 1. 属性的继承
Father.call(this, chick, duck, sheep)
// son自己的属性
this.pig = pig
}
// 2. 方法的继承:
// 注意:Son.prototype.__proto__ === Father.prototype: 子构造函数的原型对象的隐式原型指向了父构造函数的原型对象
Son.prototype = Object.create(Father.prototype)
Son.prototype.code = function () {
console.log('敲代码');
}
// 3. 手动将子构造函数的原型对象的构造器指回子构造函数本身
Son.prototype.constructor = Son
let s = new Son('鸡', '鸭', '羊', '猪')
s.farm();
s.code();
console.log(s);
// 构造器:指回构造函数本身
console.log(Son.prototype.constructor);
02-es6类的继承
`语法`:
class 子类 extends 父类 {
constructor(属性) {
// 属性的继承
super(父类的属性)
this.xxx = xxxx
}
}
`注意`:
1. 当使用了extends关键词的时候,就直接把父类的方法继承过来了
2. 当子类有自己的属性时,必须先继承,再自有
3. 静态方法属于父类自己独有,不可以被实例对象使用,也不可以被继承
`完整代码`:
class Father {
// 构造器函数:绑定对象的属性
constructor(chick, duck, sheep) {
this.chick = chick
this.duck = duck
this.sheep = sheep
}
// 原型方法: 挂载到prototype原型对象上的方法
farm() {
console.log('种田');
}
// 静态方法:构造函数独有的方法,不可以被实例对象使用,也不可以被继承
static dmj() {
console.log('打麻将');
}
}
// 类的继承:extends: 子类 extends 父类 {}
class Son extends Father {
constructor(chick, duck, sheep, pig) {
// 注意:先继承再自有
// 属性的继承:super关键字实现: super(需要继承的属性,...)
super(chick, duck, sheep)
// pig: Son自有的属性
this.pig = pig
}
// 子类自己的原型方法
code() {
console.log('敲代码');
}
}
let s = new Son('鸡', '鸭', '羊', '猪')
console.log(s);
// 1. 当使用了extends关键词的时候,就已经把父类的原型方法继承过来的
s.farm()
s.code()
s.dmj()
03-深拷贝函数的封装【笔试】
# 手写深拷贝函数
let obj = {
uname: '小妲己',
age: 18,
team: ['坤坤', '波波', '迪迪', '凡凡'],
company: {
add1: '科技一路',
add2: '科技六路',
add3: ['立人科技', 'B座']
},
un: undefined,
nu: null,
reg: /^[0-9]$/,
show: function () {
console.log('秀儿');
}
}
// 思路:遍历需要拷贝的数据类型,分为3种情况:1. 如果是数组或对象,就继续遍历,直到得到基本数据类型;2. 如果是函数,就调用bind方法复制一份即可;3. 如果是基本类型,就直接用等号赋值即可
// 1. 遍历的既可以是对象,也可以是数组,使用for...in遍历
// 2. 判断的数据类型包含了所有可能的情况,必须使用Object.prototype.toString.call()
// 1. 封装判断数据类型的方法
function getType(data) {
return Object.prototype.toString.call(data)
}
// 2. 封装对数组的判断
function isArray(data) {
return getType(data) === '[object Array]'
}
// 3. 封装对对象的判断
function isObj(data) {
return getType(data) === '[object Object]'
}
// 4. 封装对函数的判断
function isFunction(data) {
return getType(data) === '[object Function]'
}
// 5. 封装深拷贝函数
function cloneDeep(target) {
// 5.1 判断拷贝的是对象还是数组
var newTarget = isArray(target) ? [] : {}
// 5.2 遍历要拷贝的数据
for (var key in target) {
// 如果是数组或对象,就继续遍历,直到得到基本数据类型
if (isArray(target[key]) || isObj(target[key])) {
newTarget[key] = cloneDeep(target[key])
} else if (isFunction(target[key])) {
// 如果是函数,就调用bind方法复制一份即可
newTarget[key] = target[key].bind()
} else {
// 如果是基本类型,就直接用等号赋值即可
newTarget[key] = target[key]
}
}
return newTarget
}
let newObj = cloneDeep(obj)
console.log(newObj);