JS面向对象详解

来源:互联网 发布:mac怎么截gif 编辑:程序博客网 时间:2024/05/21 21:44

类理论

封装:强调数据和操作数据的行为本质上是相互关联的。好的设计就是将数据以及和它相关的行为封装(或者打包)起来。实例化:我们可以使用类对数据结构进行分类。可以把任意数据结构看作范围更广的定义中的一种实例。如“汽车”为“交通工具”的一种实例。继承:子类通过继承父类来对类进行相应的扩展。多态:父类的通用行为可以被子类用更加特殊的行为重写。注意:类理论强调父类和子类使用相同的方法名来表示特定的行为,从而让子类重写父类。但在JS代码中这样会降低代码的可读性和健壮性。

JS中的类理论

由于类是一种设计模式,在JS中可以通过一些方法实现类的功能。JS中提供了一些近似类的语法,如 new 、class等关键字。但这实质中只是一种语法糖。

类构造函数:是一种初始化一个类实例初始化所需要的所有信息的一种方法,这个方法名通常与类名相同,被称为构造函数。

class Foo{    private int a;    //构造函数    Foo(int a){        this.a = a     }    public void methods()        System.out.println("some methods");    }    public static void main(String[] args){        Foo foo = new Foo("24"); //实例化一个类    } }
 类的继承: 定义一个类,然后定义一个继承前者的类。每一个继承而来的类都是一个独一无二的存在。
 class Bar extends Foo{    public void main(String[] args){       Bar bar = new Bar();       bar.methods(); //调用继承于父类的方法。    } }
 类的多态:任何方法都可以引用继承层次中高层的方法。在继承链的不同层次中一个方法名可以被多次定义,当调用方法时会自动选择合适的定义。
 class Baz extends Bar{    public void methods(){        System.out.println("my methods defined by myself");    }    public static void main(String[] args){        Baz baz = new Baz(); //my methods defined by myself    }}  
混入和多重继承:JS中没有提供多重继承的机制。在继承或者实例化时,JS的对象机制并不提供自动执行复制行为。简单来说,JS中只有对象,并不存在可以被实例化的“类”,一个对象并不会被复制到其他对象,它们只是被关联起来。JS中使用方法来模拟类的复制行为。

显示混入

function mixin(sourceObj, targetObj){    for(var key in sourceObj){        if(!key in targetObj){            targetObj[key] = sourceObj[key];         }    }}  

隐式混入

    function SupType(name,age){        this.name = name;        this.age = age;    }    function SubType(name,age,job){        SupType.call(this,name,age);        this.job = job;    }    var o = new SubType("Fzw","23","coder");    console.log(o); // {name:"Fzw",age:23,job:"coder"}

原型

定义:JS对象中有一个特殊的[[prototype]]内置属性,实质上是对其他对象的引用。几乎所有的对象在创建时[[prototype]]都会被赋予一个非空的值。当在对象中查找某一个属性的值时,会首先在对象本身上查找,否则会在对象的[[prototype]]进一步查找。这个过程会持续到找到匹配的属性名或者整条[[prototype]]被查询完。如果是后者,则返回undefined。

[[prototype]]的尽头:所有普通的[[prototype]]链最终都会指向内置的Object.prototype。

原型继承

SubType.prototype = SupType.prototype

                                                                                                                                                                                                                                                                                                                                                                      这个等式并不会创建一个关联到SupType的新对象,它只是让SubType.prototype去直接引用SupType.prototype,当对SubType.prototype进行任何修改增删操作时,都直接修改SupType.prototype本身。

SubType.prototype = new SupType()(存在副作用的做法!)

    这种做法确实会创建一个关联到SupType.prototype的新对象。但是调用了SupType的构造函数,其存在的一些具体副作用的方法如:修改状态、注册到其他对象或给this添加属性等。会间接影响SubType 的实例。
SubType.prototype=Object.create(SupType.prototype) Object.setPrototypeOf(SubType.prototypr, SupType.prototype)
 两者实现的结果几乎一致,创建了一个无副作用且关联到SubType.prototype的新对象。区别在于前者使用于ES6之前,且存在一些性能上的损失,抛弃的对象需要进行垃圾回收。后者在ES6后可用。

检查类的关系

假设有对象Foo,如何寻找Foo对象委托的对象(假设存在)。
    function Foo(){}    Foo.prototype.name = "Foo";    function Bar(){}    Bar.prototype = Object.create(Foo.prototype);    function Baz(){}    Baz.prototype = Object.create(Bar.prototype);    var foo = new Foo();    var bar = new Bar();    var baz = new Baz();    console.log(bar instanceof Foo) //true    console.log(baz instanceof Bar) //true    console.log(baz instanceof Foo) //true    //instanceOf 只能判断在foo的原型链中是否存在指向Foo.prototype的对象

使用函数方法判断[[prototype]]的反射问题

Foo.prototype.isPrototypeOf(bar); // true    Bar.prototype.isPrototypeOf(baz); // true    Foo.prototype.isPrototypeOf(baz); // true    //和instanceof的效果一样    //使用Object.getPrototypeOf , 因代码的执行环境有差异    Object.getPrototypeOf(foo) == Foo.prototype;//true    Object.getPrototypeOf(bar) == Foo.prototype;//false    Object.getPrototypeOf(baz) == Foo.prototype;//false    //使用__proto__属性(绝大对数支持一种非标准的方法来访问内部[[prototype]]属性)    foo.__proto__ === Foo.prototype; //true    bar.__proto__ === Foo.prototype; //false    baz.__proto__ === Foo.prototype; //false    //__proto__大致实现    Object.defineProperty(Object.prototype,"__proto__",{        get:function(){            return Object.getPrototypeOf(this);        },        set:function(o){            Object.setPrototypeOf(this, o);            return this;        }    }); 

关于Object.create

//ES5新增函数,ES6polyfill如下,实现Object.create的部分功能if(!Object.create){    Object.create = function(o){        function F(){}        F.prototype = o;        return new F();    }}//实际上,Object.create还可以在对象添加属性,并配置访问属性var obj = {    a:24}var newObj = Object.create(obj, {    b:{        enumerable:false,        writable:true,        configurable:false,        value:3    },    a:{        enumerable:true,        writable:false,        configurable:false,        value:4    }});newObj.hasOwnProperty("a"); //truenewObj.hasOwnProperty("b"); //truenewObj.hasOwnProperty("c"); //falseconsole.log(newObj) //{a:3,b:4}

原型继承本质:委托

原型链机制的本质是指向对象中的一个内部链接引用另一个对象。如果在第一个对象中没有查找到相应的属性或方法引用,那么JS引擎就会继续在[[prototype]] 关联的对象上进行查找。同理,如果后者也没有找到需要的引用就会继续查询它的[[prototype]],以此类推,这一系列对象被称为原型链。