美文网首页
JavaScript 面向对象的简要总结

JavaScript 面向对象的简要总结

作者: 写优雅 | 来源:发表于2018-01-12 13:59 被阅读0次

    笔者将从如下三方面进行简要的总结:

    1. 如何理解JavaScript的面向对象思想
    2. 如何创建对象
    3. 如何继承

    如何理解JavaScript的面向对象思想?

    ECMA-262中对象的定义:“无序属性的集合,其属性可以包含基本值、对象或者函数”。

    这里将无序属性的一个集合称为对象,那么对象一定是某种数据结构,而能够容纳无序数据的最高效数据结构,最可能的就是散列表,也就是哈希表。令人困惑的是,这里没有首先引入类的概念,而是直接定义的对象,当你翻遍所有的规范,也不会发现JS有关于类的定义,这也是它与其他OO语言不同之处。

    没有了类,那应该如何定义以及创建一个对象呢?简单地说,对象由构造函数创建,而属性则可以随时动态地添加。若要深究的话,构造函数也是一个对象,它也有被创建的周期。

    JS对象相对而言还有一个属性类型的概念。ECMA-262第5版在定义只有内部采用的特性(attribute)时,描述了属性(property)的各种特征。ECMA-262定义这些特性是为了实现JS引擎用的,因此在JS中不能直接访问它们。虽然语法上JS是弱类型的语言,但是其内部类型是不是也划分得很清楚。而这些特性,你也可以理解为是类型的标签,是不是觉得有点像是C#语法中类的特性一样。只不过这里严格的说,是对象属性的标签。

    属性类型,我们需要注意的是,它分为数据属性和访问器属性两种。

    关于数据属性,举个简单例子:

    var obj = { 
        fun : function() {
            console.log('i am a function.');
        }};
    var re = Object.getOwnPropertyDescriptor(obj,'fun');
    console.log(re);
    

    将该段代码放入控制台运行后,显示如下:


    屏幕快照 2018-01-11 下午6.04.09.png

    从代码可以看出,通过对象字面量语法,obj对象的fun属性被赋值了一个匿名的函数表达式。而通过Object的getOwnPropertyDescriptor方法,可以看出这里的fun属性的特性值。需要注意的是,Value特性值存储的是匿名函数的指针(其实这里Object的值也是一个指针,它指向的是一个函数类型的对象)。

    而访问器属性则不包含数据值了,它们包含一对儿getter和setter函数(不过,这两个函数都不是必需的)。其实这非常类似于C#中类的属性。

    说到这里大家对JS的面向对象思想是不是有一个新的认识了呢。

    如何创建对象?

    C#中对象的创建是通过构造函数创建的,同理,JS中的对象也是依靠构造函数创建,JS中创建对象归纳起来主要有三个模式:

    1. 工厂模式
    2. 构造函数模式
    3. 原型模式

    工厂模式

    关于工厂模式,举个简单例子:

    function createObj(name){
        var o = new Object;
        o.name = name;
        o.printName = function(){
            console.log(this.name);
        }
        return o;
    }
    var obj = createObj('ray');
    console.log(obj);
    

    这里需要注意的: new是一个操作符,跟在new后面的是表达式,所以可以是 new Object ; new Object() ;new fun;new fun()都是可以的。而前面也稍微提过,Object其实是一个全局的函数指针,而它指向的只能是基本对象的构造函数。this类似于C++中对象this指针,它指向的永远是调用this的该对象自己。

    构造函数模式

    构造函数模式的例子:

    function Obj(name){
        this.name = name;
        this.printName = function(){
            console.log(this.name);
        };
    }
    var obj = new Obj('ray');
    var obj2 = new Obj;
    console.log(obj);
    console.log(obj2);
    

    结合前面的工厂模式,其实就是自定义了构造函数而已。而obj2调用构造函数的时候,就没有传入参数,所以显示结果是undefine。


    屏幕快照 2018-01-11 下午7.04.46.png

    其实工厂模式和构造函数的例子都很好理解,但是这两个模式是有缺点的。就是每次创建对象都要执行一次构造函数,如果构造函数创建的对象多了,显然不是很划算,要是构造函数只执行一次,后面的对象都共用这一次的方法不就妥了么?第三种模式则正好解决了这个问题。

    原型模式:

    先来看一下原型模式的例子:

    function Obj(){
    }
    
    Obj.prototype.name = "ray";
    Obj.prototype.printName = function(){
        console.log(this.name);
    }
    
    var obj = new Obj;
    obj.printName();
    

    这里先引入一个事实,就是我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有对象共享的属性,我们把它叫做原型对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,而这个属性存储着一个指回该函数的指针。而由该构造函数创建的对象则包含了一个属性__proto__,该属性也同样指向原型对象。是不是很绕,看看下面的图就清楚了。

    屏幕快照 2018-01-12 下午12.05.02.png

    那么这三种模式,我们应该怎样使用呢?其实创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义对象属性,而原型模式用于定义共享的属性。请看下面的例子。

    function Obj(name){
        this.name = name;
    }
    
    Obj.prototype = {
        constructor : Obj,
        printName : function(){
            console.log(this.name);
        }
    }
    
    var obj = new Obj('ray');
    obj.printName();
    

    这里需要注意的是,如果采用对象字面量语法的话,必须要指明constructor的值,否则会指向Object构造函数。稍微解释下就是,我们知道每次创建一个函数,都会同时创建它的prototype对象,这个对象也会自动获得constructor属性。如果使用对象字面量语法,则本质上完全重写了默认的prototype对象,因此constructor属性也就变成新的属性了。

    如何继承?

    继承是OO语言中一个最为人熟悉的概念。许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。但是,JS中的函数实为对象,是没有签名的,所以在JS中无法实现接口继承。JS只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

    原型链

    那么什么是原型链呢?原型链主要是利用原型让一个引用类型继承另一个引用类型的属性,简单理解起来就是,“我要继承一个对象,我通过原型对象来引用它就是了”。请看如下代码:

    function Father(){
        this.familyName = 'wang'
    }
    Farther.prototype.sayFamilyname = function(){
        console.log(this.familyName);
    }
    
    function Child(){
    }
    
    Child.prototype = new Father();
    
    var man = new Child();
    man.sayFamilyname();
    console.log('this is my family name : ' + man.familyName);
    

    简单用图来梳理一下:

    屏幕快照 2018-01-12 下午1.35.18.png

    可以清楚的看到,man通过引用Child的原型对象(等于Father的一个实例对象,含有_proto_指针)间接的引用了Father以及Father的原型对象。说白了,JS的继承其实就是通过指针指向来起作用的。

    相关文章

      网友评论

          本文标题:JavaScript 面向对象的简要总结

          本文链接:https://www.haomeiwen.com/subject/kukfoxtx.html