这一节,主要讲解面向对象的继承,回顾上一节讲解到判断对象原型和实例对象的两种方法.废话不多说,直接上代码 !
继承 ==> 重点
- 子对象–>父对象,Object是所有对象的父级
- 一种把其他对象的属性和方法变为己有的方式
- 通过继承可以提高代码的复用性和可维护性
- 举例 : 动物 – 狗类 – 金毛狗类
1.对于狗类来説:动物是它的父类型,而金毛狗类是它的子类型
2.对于金毛狗类来説.狗类是它的父类型,动物类是它的超类型.
继承的两种方式
- 接口继承
- 实现继承
- js中的继承:
javaScript中继承只支持实现继承,实现继承主要依赖原型链来完成
Js中继承的几种实现方式
混入式继承
- 示例代码1 :
|
|
- 示例代码2 :
|
|
原型式继承
- 原型对象中的成员(属性|方法)可以被使用该构造函数创建出来的所有对象共享
- 利用上面的性质,可以实现原型式的继承
- 示例代码1 :
|
|
- 示例代码2 :
|
|
示例代码3 :
|
|
存在的问题 :
1.构造器属性指向(默认指向的是父构造函数)
2.无法获得实例属性和方法,只能继承(获得)父构造函数原型对象的属性和方法扩展内置对象
要给数组扩展一个name的属性和一个sayHello方法- 示例代码 :
|
|
- 安全的扩展内置对象
核心过程
1.提供一个构造函数(自定义)
2.设置构造函数的原型对象为内置构造函数创建出来的对象 - 示例代码 :
|
|
- 图解

原型链继承 ( 什么是原型链 ? )
- 每个对象都是由构造函数创建出来的,因为每个对象都有构造函数
- 每个构造函数都有一个与之对应的原型对象
- 原型对象本身也是对象
- 因此,原型对象也有自己的构造函数
- 原型对象的构造函数也有自己的原型对象
- 原型对象的构造函数的原型对象也是对象,所以它也有自己的构造函数
原型对象的构造函数的原型对象的构造函数也有自己的原型对象。
以上,形成一个链式的结构,就称为是原型链原型链的顶端是Object.prototype,Object.prototype本身也是一个对象,因此也有原型对象
Object.prototype.__proto__ 是null原型链的搜索规则 :
是向上查找,如果有直接使用,如果没有,继续向上查找.直到找到原型链的顶端
原型链搜索的路径越长,查询属性所花费的时间就越多
原则:就近原型
**- 继承方法
1.提供一个父构造函数
2.提供子构造函数
3.设置子构造函数的原型对象为父构造函数的一个实例对象
4.在这个实例对象上面设置属性和方法 - 示例代码 :
|
|
复杂的原型链
1.提供构造函数(4)
2.设置属性和方法(建议:属性设置在实例上,方法设置在原型上)
3.设置继承
4.修正构造器属性示例代码 :
|
|
原型链继承的注意点
1.注意必须要在设置子对象的原型属性和原型方法之前来实现继承
2.在子对象的原型对象中可以设置同名的属性或方法,覆盖父对象的原型对象中的属性和方法
3.原型链继承之后,使用字面量的方式来设置原型会导致之前的原型对象丢失示例代码1
|
|
- 示例代码2 :
|
|
-示例代码3 :
|
|
原型链继承存在的问题
1.父类型实例属性会转换为子类型原型的原型属性,而如果父类型是实例属性是引用类型则会存在共享问题
2.在创建子类型的实例时,不能向父类型的构造函数中传递参数示例代码 :
|
|
- Object.Create
- 作用:创建对象,并且设置该对象的原型对象
- 兼容性问题:ES5
- 基本用法
- 示例代码1 :
|
|
- 实例代码2 : 若不兼容则动态添加create方法
|
|
- 示例代码3 : 使用函数来封装
|
|
call && apply函数 ==> 重点
1.这两个方法都是原型对象上面的方法 – 借用其他对象的方法
2.call和apply两个函数的作用是一样的,都可以用来借用方法
参数:
第一个参数是调用该方法的对象(函数内部的this绑定的对象)
后面的参数:
call:参数列表
apply:数组
3.调用方法
对象1.方法.call(调用方法的真正的对象,参数1,参数2,参数3);
对象1.方法.apply(调用方法的真正的对象,[参数1,参数2,参数3..]);示例代码 :
|
|
关于
this1.javaScript中的this总是指向一个对象。
2.调用方式 :
2.1作为对象的方法来调用 this—>当前的对象
2.2作为普通的函数调用 this—>window
2.3作为构造函数和new使用 this—>构造函数内部新创建的对象
2.4被call或者是apply调用(函数上下文调用) this—>第一个参数示例代码1 : 当函数作为对象的方法调用时,this指向该对象
|
|
- 示例代码2 : 当函数作为普通函数调用是,this总是指向全局对象
|
|
- 示例代码3: 使用new 构造函数创建对象调用,this总是指向内部默认创建并返回的对象
|
|
- 实例代码4:
Function.prototype.call或Function.prototype.apply调用
|
|
借用构造函数继承
- 借用构造函数实现继承:可以获得父构造函数中的实例属性,解决了传参的问题
- 示例代码 :
|
|
组合(原型链 + 借用)继承
- 借用构造函数实现继承:可以获得父构造函数中的实例属性
Person.call(this,name,age) - 获得原型属性和方法
( Boy.prototype = Person.prototype) 组合继承:原型 + 借用构造函数
示例代码 :
|
|
深拷贝和浅拷贝
- 示例代码 : 浅拷贝
|
|
深拷贝
1.提供一个函数,两个参数(原对象,要拷贝属性的对象)
2.在函数中先检查第一个参数是否有值,如果没有值那么就初始化一个空的对象
3.for..in循环来变量参数2,
3.1检查当前的属性值是什么类型
3.2如果是值类型,那么就直接拷贝赋值
3.3如果是引用类型,那么就再调用一次这个方法,去内部拷贝这个对象的所有属性示例代码 : 深拷贝封装
|
|
通过深拷贝实现继承
完整的继承方案
1.拥有父构造函数的实例属性
2.拥有父构造函数的原型属性
3.是相互独立的,彼此不受影响示例代码 :
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859<script>if(typeof Array.isArray != "function") {Array.isArray = function(obj){return Object.prototype.toString.call(obj) == "[object Array]";}}function deepCopy(obj1,obj2) {obj1 = obj1 || {};for (var i in obj2){if (obj2.hasOwnProperty(i)){if( typeof obj2[i] == "object"){//判断是数组还是对象obj1[i] = Array.isArray(obj2[i])?[]:{};//引用类型deepCopy(obj1[i],obj2[i]); //函数调用}else{//值类型obj1[i] = obj2[i];}}}}function Person(name,age){this.name = name;this.age = age;};Person.prototype.des = "描述信息";Person.prototype.car = {type:"汽车"}Person.prototype.logDes = function(){console.log(this.des);};function Boy(bookName,name,age){this.bookName = bookName;//Person.call(this,"悟空",600); //借用构造函数Person.call(this,name,age);}//设置原型继承//Boy.prototype = Person.prototype;deepCopy(Boy.prototype,Person.prototype); //把父构造函数原型对象上面的属性和方法全部拷贝一份给Boy//创建对象var boy01 = new Boy("水煮三国","悟空",600);var boy02 = new Boy("大话西游","云风",40);Boy.prototype.car.type = "火车";console.log(Boy.prototype);var p1 = new Person();console.log(p1.car.type);</script>
基本包装类型
- String Number Boolean
- 示例代码 :
|
|
- 基本包装类型注意点:
|
|
总结
1.变量的方式调用成功
2.表达式的方式调用成功
3.数值的方式调用失败示例代码 :
123456789101112131415<script>Number.prototype.sum = function(param){return this + param;}var num1= new Number("10");var num2 = 30;console.log(num1.sum(2));console.log(num1.sum(33));console.log(num2.sum(44)); //可以调用吗? 74console.log((10).sum(11)); //可以调用吗? ()表达式 21console.log(10.sum(22)); //不可以调用</script>
附思维导图一张,希望能帮到各位 ! 如果图片不清晰的话 请下载到本地查看
