Javascript-基本知识(三)

Javascript继承

1.原型对象补充:

01 构造函数有一个相关联的原型对象,这个原型对象默认是一个空对象{},该对象拥有一个constructor属性和__proto__

02 构造函数的原型对象本身是Object类型,即Person.prototype是Object类型;Person.prototype的构造函数也是Object类型。

03 使用构造函数创建出来的对象会拥有一个构造器属性(constructor),该属性指向创建当前对象的构造函数。

04对象本身并没有构造器属性,该属性是从原型对象中继承来的。

function Person(){
}
var p = new Person();
console.log(Person.prototype);   //Object
var o = {};
console.log(o);

//访问的是地址为0x11的那个constructor
console.log(Person.prototype.constructor);   //Person
//删除0x11的constructor
delete Person.prototype.constructor;   
//访问的是原型对象上面的属性
console.log(Person.prototype.constructor);   //Object 

#2.属性拷贝和直接赋值的区别:

属性拷贝和直接赋值都会拷贝成员和原型对象。

通常应用属性拷贝 拷贝实例化属性|方法

属性拷贝:只有引用类型的属性会被共享。

直接赋值:所有的属性和方法都会被拷贝。

3.继承的几种实现方法:

  1. 属性拷贝(浅拷贝)
  2. 原型式继承(3种)
  3. 原型链继承
  4. 借用构造函数
  5. 组合继承
  6. 深拷贝+原型式继承

(1)属性拷贝(浅拷贝)

属性拷贝默认情况下,使用for...in循环拷贝时,会把原型属性和原型方法一并拷贝。

通常只要求拷贝实例成员,因此需要增加个判断。

function Person() {
    this.name = "默认的名称";
    this.friends = ["123","456","567"];
}
Person.prototype.hi = "hi";
var p1 = new Person();
var o = {};
//拷贝属性
for(var k in p1)
{
    if(p1.hasOwnProperty(k))   //如果是p1的实例成员
    {
        o[k] = p1[k];
    }
}
console.log(o);
console.log(p1);

(2)原型式继承:

原型式继承:child.prototype = Parent.prototype;

  • 利用对象的动态特性

    eg:Person.prototype.des = "描述信息";

  • 字面量直接替换

    Person.prototype = { constructor:Person, des:"默认的描述信息" }

  • 设置子构造函数的原型对象 = 父构造函数的原型对象

    child.prototype = Person.prototype;

问题:第三种方式可以获得父构造函数原型对象上面的属性和方法,但无法获得父构造函数的实例对象属性和方法

补充:扩展内置对象(Array,Function,Date,Object...)

  • 把需要共享的属性和方法写在内置构造函数的原型对象上。

问题:
原型对象上面所有的属性和方法都会被该构造函数创建出来的对象共享,共享的话可能会出现覆盖问题,比较难以维护和管理。

  • 安全的扩展内置对象。
    1. 提供一个自定义的构造函数(eg:myArray)

      function MyArray(){}

    2. 设置该构造函数的原型对象为内置构造函数的一个实例

      MyArray.prototype = new Array()

    3. 在构造函数的原型对象上面添加属性和方法

      MyArray.prototype.name = "默认名称";

    4. 使用自定义的构造函数来创建对象并使用

      var myArr = new MyArray();

(3)原型链继承:

原型链继承:child.prototype = new Person();

原型链结构:

    1.js中所有的对象都是由构造函数创建出来的;
    
    2.每个构造函数都有一个原型对象;

    3.每个对象都有自己的构造函数;

    4.每个构造函数的原型都是一个对象;

    5.那么这个构造函数的原型对象也有自己的构造函数;
    
    6.那么这个构造函数的原型对象的构造函数也有自己的原型对象。

原型链的顶端:Object.prototype

JS中,Object.prototype.proto = null;

原型链结构图解

原型链搜索规则:
    对象在访问属性的属性时,先查找实例对象的成员,找不到就去它的原型对象上面查找,如果查找到Object.prototype也没有找到,就返回undefined(属性)和报错(方法)。

原型链注意点:
    1.原型和实例的关系:
        子类的原型对象为父类的一个实例。

    2.注意重写原型对象的位置:
        
        - 先重写原型对象,然后再覆盖超类的方法。(eg:children.prototype.constructor = Children)
        
        - 建议在完成原型链继承之后再给原型对象设置属性和方法。
        - 在设置完原型链继承之后,不能用字面量的方式来替换当前构造函数的原型对象。

问题:

1. 无法向父构造函数传递参数;

2. 父构造函数的实例属性变成了子构造函数创建出来对象的原型属性。如果父类型的实例属性是引用类型,则会存在共享问题。


补充:Object.Create()

Object.create():创建对象,并指定原型对象

eg:var o = Object.create(obj); //创建一个对象o,并指定这个对象的原型对象为obj

注意:该方法是ES5推出的,所以有兼容性问题。

Object.assign()

Object.assign(target,source1,source2...)

作用:拷贝多个对象的属性。

for...in:会连原型成员也拷贝
assign:不会拷贝对象的原型成员

(4)借用构造函数继承:

借用福构造函数获得父构造函数的实例属性。

Parent.call(子构造函数,父构造函数的实例属性)

不足:无法获得父构造函数的原型成员。

(5)组合继承:

借用父构造函数,获取父构造函数的实例对象 + 原型式继承

原型式继承:Child.prototype = Parent.prototype;

借用父构造函数获取实例对象的不足:无法获得父构造函数的原型成员;

原型式继承不足:无法获得父构造函数的实例成员。

深拷贝

浅拷贝:for...in 引用类型会共享
深拷贝是对浅拷贝进行了一些改进

分析:浅拷贝只是用了一层for循环,当前拷贝的属性是个引用类型时,就只是把这个引用类型的地址复制了一份。此时就会存在共享问题。

所谓深拷贝就是进行多次浅拷贝,
(0)for in循环遍历当前对象
(1)先查找当前对象是否存在该属性
(2)判断属性是否为引用类型
(3)如果是引用类型,就再次递归,查看引用类型中的属性是否有值类型
(4)有的话就赋值一份值,如果没有就循环(3)
(5)直到找出所有的值,将值拷贝给另一个对象

(6) 深拷贝+原型式继承:

call|apply:借用父构造函数获得父构造函数的实例成员

深拷贝:将父构造函数原型对象上面的属性和方法拷贝给子构造函数的原型对象(原型式继承)

<script>
    function deepCopy(obj1, obj2) {
    obj1 = obj1 || {};
    for (var k in obj2) {
        if (obj2.hasOwnProperty(k)) {
            if (typeof obj2[k] == "object") {
                obj1[k] = Array.isArray(obj2[k]) ? [] : {};
                deepCopy(obj1[k], obj2[k]);
            } else {
                obj1[k] = obj2[k];
            }
        }
    }
}
function Person(name) {
    this.name = name;
}
Person.prototype.hi = "hi";
Person.prototype.car = {
    type:"奥迪"
};
function Boy(name, lol) {
    //构造函数内部会默认创建一个空的对象,并且赋值给this
    this.lol = lol;
    Person.call(this,name);        //Boy借用Person构造函数中的实例属性,
                                    // 当Boy借用Person的实例属性时,Person里的this->Boy构造函数创建的对象,这里的name就是Person中的形参
}
//子构造函数的原型对象 = 父构造函数的原型对象
deepCopy(Boy.prototype, Person.prototype);
var p1 = new Person();
var boy = new Boy("张三","英雄联盟");
console.log(p1.car);
console.log(boy.car);
p1.car.type = "宝马";
console.log(p1.car);  //宝马
console.log(boy.car);   //奥迪

</script>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容