码迷,mamicode.com
首页 > 编程语言 > 详细

深入javascript面向对象,js的原型链、继承

时间:2015-08-15 10:29:13      阅读:220      评论:0      收藏:0      [点我收藏+]

标签:javascript   面向对象编程   面向对象   对象   继承   

进阶面向对象——————————————————————–

在JS源码中,系统对象也是基于原型的程序,
尽量不要去添加和修改系统对象的方法

包装对象——————————————————————–

基本类型都有自己对应的包装对象
比如String Number Boolean
基本类型会找到对应的包装对象类型,然后包装对象把所有的属性方法给了
基本类型,然后包装对象消失

例如
        var str = ‘abc‘;
        str.num = 10;
        //创建一个包装对象,给包装对象加num属性,然后立刻消失。
        //除非设置String.prototype.num = 10;
        alert(str.num);
        //这时候又创建了一个包装对象,和上面创建的那个没有关联,
        //所以显示undefined

原型链————————————————————————-

实例对象与原型之间的连接,叫做原型链

在ff中的DOM能查看到:__proto__( 隐式连接 )

对于一个对象来说,调用其方法或使用其属性,它会先看自己有没有该
方法/属性,如果没找到,再通过__proto__向上查找它的原型,直至
最顶层的原型Object

Object对象类型是原型链的最外层
    Object.prototype

面向对象的一些常用属性和方法—————————————————

obj.hasOwnProperty(‘name‘) : 查看对象/属性是否是对象本身的。
    该方法是 Object.prototype的方法

constructor : 查看对象的构造函数
    还可以用来判断对象的类型 如: 
            var str = ‘‘;
            alert(str.constructor===String);//true
    另外,当声明一个构造函数时,系统会自动给xxx.prototype.constructor
    赋值,如:
        function Person(){
        }
    系统会自动加上 Person.prototype.constructor=Person;
    而且这是面向对象系统自动生成的唯一一段代码

    但是如果用JSON
    Person.prototype = {
        attr1 : 10,
        fn1 : function(){}
    }
    来给Person加对象加属性/方法的话,会覆盖系统自动生成的代码。
    导致对象实例化以后,实例化对象.constructor 会通过原型链
    (因为本构造函数没有这个属性)找到Object.constructor,从而
    得到的值是Object

    因此用JSON给对象写原型属性/方法时,要先给对象写construct
    属性,并指向本身

    For in 的时候有些系统自带属性prototype属性是找不到的
    (自己添加的找得到)

    避免修改construtor属性

instanceof : 对象与构造函数在原型链上是否有关系
    用法:
    person1 instanceof Person 在原型链上有关系就返回true

    也可以用来判断对象是否是指定的属性
    var arr = [];
    arr instanceof Array //true

toString() : 
    把对象转为字符串

    是Object上的方法
    系统对象下面的这个方法都是自带的,而自己写的对象都是通过
    原型链找到Object下面的方法

    Array对象的toString:返回数组的字符串形式

    Number对象的toString(n):返回数字n进制的字符串形式

    利用toString做类型的判断(最好)
    var arr=[];
    alert( Object.prototype.toString.call(arr) );//[Object Array];

    Object.prototype.toString.call(arr)===‘[Object Array]‘;
    为真则是数组

总结判断对象类型的三种方法:
        obj.constructor === Array;
        obj instanceof Array;
        //以上两个在跨iframe,判断window.frames[i].Array;时不行
        Object.prototype.toString.call(obj) === ‘[Object Array]‘; 

继承—————————————————————————

概念 : 在原有对象的基础上,略作修改,得到一个新的对象
        不影响对象原有功能。
好处 : 子类不影响父类,子类可以继承父类的一些功能 (代码复用)

写法 : 
    属性继承:调用父类的的构造函数.call():
        function Person(name,sex){
            this.name = name;
            this.sex = sex;
        }
        function Star(name,sex,job){
            Person.call(this,name,sex);
            //如果不改变this指向,function下的Person中的this指向window
            this.job = job;
        }
    原型方法/属性的继承:
    【拷贝继承】
    父类的原型拷贝一份赋给子类,
    这样就不会因为JS的引用机制,子类修改的原型属性方法影响父类
    Star.prototype = deepCopy(Person.prototype);

    【类式继承】  
    JS中没有类的概念,可以把构造函数等价于java中的类

    类式继承写法:
        function Father(){
            this.name = ‘大明‘;
        };
        Father.prototype.callname = function(){
            alert(this.name); 
        };
        function Child(){};
        Child.prototype = new Father();//核心
        var c1 = new Child();
        c1.callname();//大明
    原理:c1本身没有callname方法,于是找Child上的callname方法
    Child.prototype上面也没有callname方法,于是找到new Father()
    这个匿名对象上,这个实例对象本身也没有该方法,要通过原型链找到
    Father.prototype上的callname方法,然后再调用

    问题:
    Child.prototype = new Father();一句话覆盖了Child所有的prototype
    ,就连Child.prototype.constructor也被覆盖了,所以
    c1.constructor也找不到了,要通过原型链找到其父级的constructor,
    所以c1.constructor的值是Father。

    因此为避免这个问题要手动修正Child.prtotype.constructor=Child;

    此外,把this.name改为数组[1,2,3];
    创建一个实例对象对其进行操作
    var c1 = new Child();
    c1.name.push(4);

    var c2 = new Child();
    alert(c2.name);//1,2,3,4
    由此发现两个不同的实例对象居然会相互之间产生影响
    原因是无论是c1还是c2,他们在读取name属性时都是在本身找不到,
    而要通过原型链到匿名实例对象new Father()上找。所以操作的
    是同一个地址。进而产生影响。

    要避免这个问题,就要做到属性和方法分开继承。
    function F(){};//1
    F.prototype = Father.prototype;//2
    Child.prototype = new F();//3
    Child.prototype.constructor = Child;//4
    //以上是类式继承的4句话
    1句中声明的F构造函数没有属性。2句中F只继承了Father的原型方法,
    自己并没有属性。3句中把一个F的实例对象赋值给Child的原型。

    这样,如果实例化Child->c1调用c1.name,只能向上找F的实例对象上
    有没有name这个属性,而由2句可知,F的实例对象是没有name这个属性
    的,所以系统只会返回undefined
    而如果c1调用方法callname,系统会通过原型链一直找到Father.prototype
    从而调用Father.prototype上的callname方法。所以属性和方法继承
    被分开了。

    而继承属性的方法还是在Child构造函数中调用Father.call(this);

    【原型继承】
        function Father(){
            this.name = ‘父亲‘; 
        }
        var f1 = new Father();

        var c1 = cloneObj(f1);
        c1.name = ‘小米‘
        //alert(c1.name)//小米
        //alert(f1.name)//父亲

        var a = {
            name : ‘a‘
        };
        var b = cloneObj(a);
        b.name = ‘b‘;
        alert(b.name);
        alert(a.name);

        function cloneObj(obj){
            function F(){};
            F.prototype = obj;
            return new F();
        }
    【三种继承方式总结】
    拷贝继承:通用型的 有new或无new的时候都可以使用

    类式继承:适合new 构造函数

    原型继承:适合无new的形式

版权声明:本文为博主原创文章,转载还请留言。

深入javascript面向对象,js的原型链、继承

标签:javascript   面向对象编程   面向对象   对象   继承   

原文地址:http://blog.csdn.net/u014420383/article/details/47678929

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!