主要写一些关于继承和拷贝相关的知识点,准确来说,ES5的继承方法不算方法,是前辈们的经验之谈,ES6的方法是JS推出的,关于深浅拷贝的话,深拷贝在近段时间的面试中出现的频率应该还行。
ES5继承
1,原型继承:
利用自定义原型的方法去继承,当Stu这个类继承了Person这个类的时候,我们把Stu叫做Person的子类。
核心:子类的原型指向父类的实例化对象
优点:实现了继承; 缺点:继承了属性,但不是在自己身上,失去了自己的原型。
(代码解析:先去函数内部查找name属性,然后发现对象内部没有这个属性,再去对象内部的 _ proto __上查找,对象的 _ proto __指向了自己的构造函数的原型,也就是Stu.prototype;因为我们手动修改了Stu.prototype ,给它赋值为Person构造函数的实例化对象,也就是说我们Stu.prototype指向了Person的实例化对象,从而达到继承的效果)。
function Person(name) {
this.name = name
}
Person.prototype.sayName = () => {
console.log('name')
}
function Stu(age) {
this.age = age
}
Stu.prototype.abc = () => {
console.log(123)
}
Stu.prototype = new Person('QF001')
Stu.prototype.abc2 = () => {
console.log(456)
}
const s = new Stu(18)
// 需求 使用 Person 内部的 name 何 sayName 方法
console.log(s)
console.log(s.age)
console.log(s.name) // QF001
2,借用构造函数的继承:
核心:借用call方法修改,父类构造函数内部的this指向。
优点:将属性继承在自己身上,保留了自己的原型。
缺点:只能继承父类的属性,不能继承父类原型上的方法。
(代码解析:通过new关键字调用Stu这个构造函数,new关键词会在这个函数内部创建一个空对象,并且会把这个函数内部的this指定刚才创建的空对象;给这个对象添加一个age属性,并给他赋值为参数 age;调用Person函数,并通过call方法改变这个函数的this指向,this指向刚才new关键词创建的对象;Person函数开始执行,给这个对象太内疚一个abc属性,并赋值为name,函数执行完毕,new关键词会将刚创建的对象return,将这个对象保存在变量s中)
function Person(name) {
this.abc = name
this.name = name
}
Person.prototype.sayName = () => {
console.log('name')
}
function Stu(age) {
this.age = age
Person.call(this, 'QF002')
}
const s = new Stu(18)
s.sayName()
3,组合继承:
*利用原型继承 继承到父类的 原型上的方法;
*利用借用继承 继承到父类的 构造函数上的方法;
优点:能够继承到属性,并且是在自己对象内部;能够继承到父类原型上的方法.
缺点:在原型上有一套多余的属性
function Person(name) {
this.abc = name
this.name = name
}
Person.prototype.sayName = () => {
console.log('name')
}
function Stu(age) {
this.age = age
// 2. 利用 借用继承 继承到 父类的 构造函数内部的 属性
Person.call(this, 'QF001')
}
// 1. 利用 原型继承 继承到 父类的 原型上的方法
Stu.prototype = new Person()
const s = new Stu(18)
console.log(s)
console.log(s.name)
//查找对象内部属性的时候, 会先在对象内部查找, 找到直接使用, 并停止查找
s.sayName()
4,拷贝继承:
核心:通过for in 遍历父类实例化对象上的所有属性,拷贝到子类上;能够遍历对象内部的属性,以及原型上。
function Person(name) {
this.name = name
}
Person.prototype.sayName = () => {
console.log('name')
}
function Stu(age) {
this.age = age
const p = new Person('QF001')
for (let key in p) {
// console.log(key, p[key])
Stu.prototype[key] = p[key]
}
}
Stu.prototype.abc = () => {
console.log(123)
}
ES6类的继承
在书写子类的时候,语法:class 子类类名 extends 父类类名 {};
在书写子类constructor 需要在内部书写super() //两个语法必须同时存在,super()必须在constructor的开始位置。除了能够继承class类,还能继承ES5的构造函数。
function Person (name) {
this.name = name
}
Person.prototype.sayName = function () {
console.log('name')
}
class Stu extends Person {
constructor(age) {
super('QF001')
this.age = age
}
}
const s = new Stu(18)
深浅拷贝:
一定是引用数据类型,对象,数组,函数(基本只有对象何数组);
赋值:只要是引用数据类型,那么在赋值时,就是引用地址的传递;
1,浅拷贝:
遍历对象拿到没一个key与value,然后赋值给另外一个对象,如果所有的value都是基本数据类型,那么浅拷贝完成之后,修改新的对象不会影响到老对象,如果value中有引用数据类型(出现了多层数据结构),那么浅拷贝一层只能拷走第一层数据结构,多层的没有办法处理。
JS提供的方法:Object.assign(新对象,原始对象) : 将原始对象中所有的key全部浅拷贝到新对象中,然后返回一个对象。
let o1 = {
a: 1,
b: 2,
c: 3,
}
let o2 = {}
for (let key in o1) {
o2[key] = o1[key]
}
console.log(o1) // {a: 1, b: 2, c: 3}
console.log(o2) // {a: 1, b: 2, c: 3}
console.log(o1 === o2) // false
//JS提供的方法
let o2 = Object.assign({}, o1)
2,深拷贝:
不管数据有多少层数据结构,百分百复制出一份,一模一样但是毫不相关的数据。
let o1 = {
a: 1,
b: 2,
c: 3,
d: {
d1: '001',
d2: '002',
d3: '003'
}
}
let o2 = {}
function deepClone(target,origin) {
for(let k in origin) {
if(origin[k].constructor === Object){
target[k] = {}
deepClone(target[k],origin[k])
}else if(origin[k].constructor === Array){
target[k] = []
deepClone(target[k],origin[k])
}else {
target[k] = origin[k]
}
}
}
deepClone(o2,o1)