JS实现继承的方式
- 1.属性拷贝
- 2.原型式继承
- 3.原型链继承
- 4.借用构造函数继承|伪对象继承|经典点继承
- 5.组合继承
- 6.Object.create()
- 7.完全拷贝
属性继承
- 混入式继承/ 属性拷贝(两种方式)
- 特点: 属性是引用类型,数据会共享
- for --- in 循环
var obj = {
name :'zs'
,age:20,
friends:['小明','小红']
}
var obj1 = {};
for(var key in obj){
obj1[key] = obj[key]
}
obj1.friends.push('老王')
console.log(obj1);
console.log(obj);
// 如果属性是引用类型的数据,子对象和父对象会共享同一份数据,修改其中一个,会影响另外一个
- 函数拷贝属性/ 相当于函数封装了for..in
- ES6 后才开始支持
- 语法Object.assign(o,obj,obj1)
- 第一个参数传目标对象
- 后面参数是要拷贝属性的对象
var obj = {name:'zs'};
var obj1 = {age:20};
var o = {};
// 第一个参数:目标对象
//后面参数:要拷贝属性的对象
Object.assign(o,obj,obj1);
console.log(o);
原型式继承
特点:
- 1.不能获取实例属性和实例方法,只能获取父构造函数原型对象的属性和方法
- 2.无法修正构造器属性,默认指向父构造函数
设置子构造函数的原型对象是父构造函数的原型对象---->原型式继承
- 1.构造函数创建对象访问原型属性
function Person() {
}
// 利用构造函数创建出来的对象可以使用原型对象的属性和方法
Person.prototype.des = 'des';
var p1 = new Person();
console.log(p1.des);
- 2.替换原型
- 3.原型对象替换原型
//1.提供一个父构造函数
function Person() {
this.name = '默认';
}
Person.prototype.des = 'aaa';
//2.提供一个子构造函数
function stu() {
}
// 设置子构造函数的原型对象是父构造函数的原型对象---->原型式继承
stu.prototype = Person.prototype;
var s1 = new Stu();
console.log(s1.des);
扩展内置对象
- 给所有内置的对象添加属性或方法
- Object Array Date Function String..
// 需求 arr1有一个des属性和logDes方法
var arr1 = [1,2,3,4];
arr1.des = 'des';
arr1.logDes = function () {
console.log(this.des);
}
console.loe(arr1.des);
var arr2 = [5,6,7];
arr2.des = 'des';
arr2.logDes = function () {
console.log(this.des);
}
// 需求 所有数组都添加一个des属性和logDes方法
Array.prototype.des = 'des';
Array.prototype.logDes = function() {
console.log(this.des);
}
var arr3 = [3,4,5];
var arr4 = [6,7,8];
console.log(arr3.des);
console.log(arr4.des);
//通过这种方式确实可以扩展内置对象,但是不建议这样做
// 因为在公司开发中,很可能是多人一起开发,而且项目的代码量可能非常多
//如果人人都通过这种方式扩展内置对象,不方便项目日后的维护,很容易出现方法被覆盖的问题
安全的扩展内置对象
- 1.提供一个构造函数
- 2..设置这个构造函数的原型对象是内置构造函数的一个实例
function MyArray() {
}
MyArray.prototype = []//new Array();
MyArray.prototype.des = 'des';
MyArray.prototype.logDes = function (){
console.log(this.des);
}
var myarr1 = new MyArray();
myarr1.push('小明');
console.log(myarr1);
console.log(myarr1.des);
;
原型链(画图理解)
- 1.每一个对象都是由构造函数创建的
- 2.每一个构造函数都有对应的原型对象
- 3.原型对象也是一个对象,也是由构造函数创建出来的
- 4.这个构造函数也有自己的原型对象,这个原型对象也是有个对象,也是由构造函数创建出来的
- 以上就形成一个链式的结构,称为原型链
- 原型链的顶端是Object的原型对象
- (Object的原型对象也是由构造函数创建出来的,它的原型对象指向null)
原型链中属性的访问原则(就近原则)
- 通过对象.属性访问属性的时候,首先会查找自身,如果有就直接使用
- 如果没有,会查找原型对象,如果有就直接使用
- 如果原型对象也没有,会查找原型对象的原型对象,如果有就直接使用
- 如果还没有会继续向上查找,知道原型链的顶端(Object.prototype)
- 如果还没有,返回undefined(属性)或者报错(调用方法)
原型链继承
特点
- 1.对比原型式继承,他还可以继承父构造函数的实例属性,方法和原型对象
- 2.对比原型式继承,他可以修正构造器属性constructor
- 1.提供一个父构造函数和子构造函数
- 2.设置子构造函数的原型的对象好似父构造函数的一个实例 --->原型链继承
//1.提供一个父构造函数和子构造函数
function Person() {
this.name = '默认';
}
Person.prototype.des = 'des';
function Stu() {
}
// 2.原型链继承
Stu.prototype = new Person();
var s1 = new Stu();
console.log(s1.des);//des
console.log(s1.name);//默认
原型链注意点
- 1.注意修正构造器要在原型链继承之后(涉及到替换对象的地址变更)
- 2.同理,要给当前对象的原型对象设置属性或方法,要在设置原型链继承之后
- 3.在原型链继承后,只能在原型链的基础上动态添加和修改属性和方法,如果用替换对象会破坏原型链继承
原型链继承的问题
- 1.无法传递参数给父构造函数
- 2.继承过来的实例属性会变为原型属性,就有数据共享的问题
Object.create()方法
- 创建一个新的对象,并设置这个对象的原型对象
- ES5 支持
var obj = {name:'zs',age:20};
//创建一个新的对象,并设置这个对象的原型对象
var o = Object.create(obj);
console.log(o.name);
- 兼容性处理
var obj = {name:'zs',age:20}; // 兼容性处理 if(Object.create){ var o = Object.create(obj); }else{ // 非标准方法 //var o = new Object(); //o._protp_ = obj; // 标准方法 可封装 function F() { } F.prototype = obj; var o = new F(); }
- 兼容性函数封装
function createObj(obj){ if(Object.create){ return Object.create(obj); }else{ function F(){ } F.prototype=obj; return new F() } }
- 方法二:处理兼容性 给Object添加方法(要放在使用方法前面)
if(!Object.create){ Object.create = function (obj){ function F(){ } F.prototype=obj; return new F() } }
call和apply函数
-
1.在ES3的时候,系统给Function的原型队形添加了call和apply函数
-
2.作用:借用其他对象的方法
-
call(调用对象,参数1,参数2(参数类表)
- 语法: 借用对象.借用方法.call(调用对象,参数1,参数2(参数类表)
-
apply(调用对象,参数数组)
- 语法: 借用对象.借用方法.apply(调用对象,参数数组)
var zs = {
name:'zs',
showName : function (p1,p2){
console.log(this.name,p1,p2)
}
}
var ls = {
name: 'ls'
}
zs.showNam('憨厚','耿直');
// 无法直接访问
//ls.showNam('智商高','情商低') // 报错
zs.showName.call(ls,'智商高','情商低')
zs.showName.apply(ls,['智商高','情商低'])
借用构造函数继承 | 经典继承 | 伪对象继承
- 只能获取实例属性和实例方法,不能获取原型属性和属性方法
- 本质是借用执行父构造函数的函数把this传入
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.des= 'des';
function stu(num,name,age){
this.num = num;
// 借用构造函数继承
Person.call(this,name,age)
}
var s1 = new stu(10086,'zs',20);
var s2 = new stu(1002,'ls',22);
console.log(s1);
console.log(s2);
console.log(s1.des);
console.log(s2.des);
组成继承
特点(利用借用构造函数继承)
- 1.解决了原型链参数传递给父构造函数的问题
- 2.解决了原型链继承所得的原型对象的数据共享问题
- 1.利用借用构造函数继承获取实例属性和方法
- 2.再获取原型属性和方法
function Person(name){
this.name = name;
}
Person.prototype.des= 'des';
function stu(num,name){
this.num = num;
// 借用构造函数继承
Person.call(this,name)
}
// 原型式继承
Stu.prototype = Person.prototype
// 原型链继承
Stu.prototype = new Person()
Stu.prototype.constructor = stu;
var s1 = new stu(10086,'zs');
console.log(s1.name);
console.log(s1.des);
深拷贝和浅拷贝
- 浅拷贝(地址拷贝)(指针拷贝)
- 引用类型的属性,把地址拷贝
- 深拷贝(内容拷贝, 完全拷贝)
- 引用类型的属性,拷贝内容,过程要实现
- 1.提供一个函数,有2个参数(目标对象,要拷贝属性的对象)
- 2.判断第一个参数是否有值,如果没值,就初始化一个空点的对象
- 3.遍历第二参数,判断属性值的类型
- 1.值类型的,直接赋值
- 2.引用类型的,再一次调用这个方法去拷贝存储的内容
function deepCopy(obj,copyObj){ obj = obj || {}; for(var key in copyObj){ if(typeof copyObj[key] == 'object'){ obj[key] = {}; //引用类型的数据 deepCopy(obj[key],copyObj[key]) }else{ // 值类型 obj[key]=copyObj[key] } } }
Array.isArray()
-
判断一个对象是否是数组
-
ES5支持
-
在ES5之前是怎么判断一个对象是数组?
Object.prototype.toString.call(判断对象)// [object,Array] -
兼容性的处理
if(!Array.isArray){ Array.isArray = function (obj){ return Object.prototype.toString.call(obj); } }
网友评论