1、含义:继承就是获取存在对象已有属性和方法的一种方式
2、继承关系出现在构造函数和构造函数之间
3、 构造函数A 继承自 构造函数B
1、原型继承
2、借用构造函数继承
3、组合继承
4、类(ES6)的继承
5、拷贝继承....
1、核心: 让子类的原型指向父类的实例,即把父类的实例赋值给子类的原型
2、优点: 父类构造函数体内的属性和原型上的方法都可以实现继承
3、缺点:
1、核心:把父类构造函数当做普通函数调用, 并且改变其 this 指向
2、优点:
3、缺点:父类的原型上的方法没有继承下来
// 父类function Person(name, age) {this.name = namethis.age = age}Person.prototype.play = function() {console.log('玩游戏')}// 子类function Student(classRoom, name, age) {this.classRoom = classRoom// 实现继承Person.call(this, name, age)}const s = new Student(2114, 'Jack', 18)console.log(s)const s2 = new Student(2115, 'Rose', 20)console.log(s2)
1、核心:把原型继承和借用构造函数继承放在一起使用
2、优点:
3、缺点:子类的原型上多了一套属性,无法去除,若实例化的过程中没有赋值,结果是undefined
4、这样的继承方式在我们es6出现之前,我们也称之为完美继承
// 父类function Person(name, age) {this.name = namethis.age = age}Person.prototype.play = function() {console.log('玩游戏')}// 子类function Student(classRoom, name, age) {this.classRoom = classRoom// 借用继承// 目的: 把属性继承在自己身上Person.call(this, name, age)}// 原型继承// 目的: 继承父类原型上的方法Student.prototype = new Person()// 创建子类的实例const s = new Student(2114, 'Rose', 20)console.log(s)
1、类的继承是ES6中提出的一种继承方式
2、实现步骤:
第一步:
第二步:
注意:(1)必须要书写 super 和 extends
(2)在子类的 constructor 内 super 必须写在 this.xxx 的前面(最前面)
(3)父类可以是构造函数,但是子类不能的构造函数因为extends和super关键字就是给类设计的
// 父类class Person {constructor(name, age) {this.name = namethis.age = age}play() {console.log('玩游戏')}}// 父类// function Person(name, age) {// this.name = name// this.age = age// }// Person.prototype.play = function () { console.log('玩游戏') }// 子类class Student extends Person {constructor(classRoom, name, age) {super(name, age)this.classRoom = classRoom}study() {console.log('学习')}}const s = new Student(2114, 'Jack', 18)console.log(s)class Teacher extends Person {constructor(gender, name, age) {super(name, age)this.gender = gender}}const t = new Teacher('男', 'Jack', 20)console.log(t)
1、核心:利用 for in 循环遍历对象,把所有的内容复制一份放在子类的原型上
2、in
注意:in这个关键字不光能检测自己身上的,也可以检测原型对象上的;
延伸:hasOwnPrototype() :也是用来检测这个键是不是在对象上的
=>语法:对象.hasOwnPrototype('对象名')
=>返回值也是布尔值
注意:这个方法只能检测自身的,不能检测原型对象上的
function Person(name){this.name = name }const p = new Person('aa')// 使用in检测对象p中是否有这个键名 nameconsole.log('name' in p); //trueconsole.log('age' in p); //false// 在原型上添加一个属性Person.prototype.age = 18// 检测原型对象上console.log('age' in p);//true// console.log(p.hasOwnPrototype('name')); //true// console.log(p.hasOwnPrototype('age')); //false
3、拷贝继承
function Person(name,age){this.name = name this.age = age }Person.prototype.sayHi = function(){console.log(111);}// 子类继承我们的父类function Student (classdom,name,age){this.classdom = classdom// 这里实现我们的继承// 首先实例化我们的父类对象let p = new Person(name,age)// 使用for in循环for(let k in p){// 把我们对象上的键和值赋给子类的原型对象Student.prototype[k] = p[k]}}// 实例化一个子类let s = new Student(2217,'aaa',22)console.log(s);
4、 原生的深浅拷贝
=> 深浅拷贝指的是一种复制对象或者数组的行为
=> 也就是把一个对象或者数组中的数据完完整整的复制一份放到另一个数组或者对象中
=> 并且相互之间没有联系,修改一个另一个不受影响
=> 说道深浅拷贝这个我们不考虑基本数据类型
=> 因为基本数据类型没有引用地址一说
=> 说到复制这个事儿 有三个级别
-> 赋值
-> 浅拷贝
-> 深拷贝
赋值
=> 就是把一个变量存储的内容复制一份给另一个变量
=> 基本数据类型赋值以后两个变量之间没有关系
=> 修改任何一个的值都不会影响另一个
=> 复杂数据类型赋值以后,两个变量操作一个空间
=> 修改一个的值另一个也会跟着改变
// 赋值// 基本数据类型let num = 100let num1 = numconsole.log(num, num1);num1 = 200console.log(num, num1);// 复杂数据类型let o1 = { name: 'Jack', age: 25 }let o2 = o1console.log(o1, o2);o2.name = 'Rose'console.log(o1, o2);
浅拷贝
=> 说到浅拷贝就不考虑基本数据类型了
=> 就是按照原先的数据类型创建一个新的一样的数据类型
=> 把原始数据中的每一个数据依次赋值到新的数据类型中
=> 通过分析我们发现浅拷贝只能拷贝一层
=> 并且是简单数据类型 如果有第二层甚至多层就不能实现完完整整的复制
=>我们修改其中一个值的时候,另一个依旧会受到影响,所以不能多层拷贝
// 浅拷贝const o1 = {name: 'Jack',age: 25,color: {red: '红色',blue: '蓝色'}}// // 实现拷贝// // 首先创建一个新的数据const o2 = {}// 遍历循环原始数据类型for (let k in o1) {// 接下来就是把原始数据类型中的数据赋值到新创建的数据类型中// 如果这个时候元素对象中还复杂数据类型// 同样是赋值 依然不能实现完完整整的复制o2[k] = o1[k]}console.log(o1, o2);// 修改o1中的值o1.color.red = '红颜色'console.log(o1, o2);
深拷贝
第一种:递归
=>依旧是不考虑我们的基本数据类型
=> 就是要百分百的拷贝一份原始数据
=> 修改任何里面的数据都不会影响另一个里面的数据
=> 不管层级有多深 都能完整的复制过来 两个之间没有关系
=> 这就要通过深拷贝
=> 深拷贝的核心:递归
实现步骤:
// 准备数据const o1 = {name: 'Jack',age: 18,info: {height: 180,weight: 180,desc: {message: '今天天气很好'}},address: {city: '北京'},hobby: ['吃饭', '睡觉']}// 准备一个空对象const o2 = {}// 要实现深拷贝 我们就准备一个函数function deepCopy(o2, o1) {// 首先我们还是要通过循环遍历的方式拿到原始数据类型中的数据for (let k in o1) {// 因为我们不知道里面都有什么数据所以我们要先判断if (Object.prototype.toString.call(o1[k]) === '[object Object]') {// 代码能进入到这里说明是一个对象o2[k] = {}// 再次进行拷贝 这个过程和我们刚做的是不是一样// 那我就直接调用我们的deepCopy函数就好deepCopy(o2[k], o1[k])} else if (Object.prototype.toString.call(o1[k]) === '[object Array]') {// 代码能执行到这里说明是一个数组o2[k] = []// 继续执行我们的拷贝函数deepCopy(o2[k], o1[k])} else {o2[k] = o1[k]}}}// 使用deepCopy(o2, o1)// console.log(o1, o2);o1.info.height = 300console.log(o1, o2);
第二种:JSON
=>把你要拷贝的数据转成 JSON格式字符串
// 这是我们要拷贝的数据const o1 = {name: 'Jack',age: 18,info: {height: 180,weight: 180,desc: {message: '今天天气很好'}},address: {city: '北京'},hobby: ['吃饭', '睡觉',[10, 20]]}// 就是把你要拷贝的数据转成 JSON格式字符串let jsonO1 = JSON.stringify(o1)console.log(jsonO1);// 之后在转换回来let o2 = JSON.parse(jsonO1)console.log(o2);// 就是验证我们的拷贝结果了console.log('原始数据(对象) :', o1);console.log('拷贝完毕后的数据(对象) :', o2);