JS高级 03

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);
        }
    }
    

你可能感兴趣的:(JS高级 03)