zoukankan      html  css  js  c++  java
  • S1:原型继承

    一、基本性质

    function obj(){
    	this.name1 = '可以被delete删除';
    }
    
    obj.prototype.name2 = '不能被delete删除';
    obj.prototype.name = '成功调用getName()方法';
    
    obj.getName = function(){
    	console.log(this.prototype.name);
    }
    
    var o = new obj();
    
    delete o.name1
    //输出:'undefined', 成功删除属性name1
    console.log(o.name1);
    
    delete o.name2
    //输出:'不能被delete删除',因为对象没有原型,访问不到属性name2
    console.log(o.name2);
    
    //报错:‘Uncaught TypeError: undefined is not a function’,因为,当obj被实例化为o时,obj 就把它的 prototype 赋给了 o 的 __proto__,并且 o 的 constructor 指向了 obj
    o.getName();
    
    //输出:'成功调用getName()方法',因为getName方法是挂载在o.constructor上的
    o.constructor.getName();
    
    

    原型

    结论:对象实例(Object Instance)没有原型,而构造器(Constructor)有(<constructor>.prototype指向原型),对象没有“持有某个原型”的问题,只有“构造自某个原型”的问题。

    //取原型对象
    proto = Object.prototype;
    //列举对象成员并计数
    var num = 0;
    
    for (var n in proto) {
    	num++;
    } 
    
    //显示计数:0
    alert(num);
    

    结论:object()构造器的原型是一个空的对象。

    obj1 = new Object();
    obj2 = {};

    那么,obj1和obj2也是“空的对象”,因为它们都是从Object.prototype复制出了一个“对象”的映像来。

    clip_image004

    结论:空的对象是所有对象的基础

    //弹出 Object
    alert(typeof null);
    
    //false
    alert(null instanceof Object);
    
    //true
    alert({} instanceof Object);
    
    //true
    alert(Object instanceof Object);
    
    

    注意:空对象(null) != 空的对象

    空对象(null):是一个保留字,它属于对象类型,但这个对象是空值的,它没有方法和属性。

    空的对象:是一个标准的,通过Object()构造的对象实例。此外,对象直接量也会隐性的调用Object()构造实例。空的对象具有对象的一切特性,它可以存取预定义属性和方法(如toString、valueof等),instanceof运算也会返回true

    小结

    原型的含义指:如果构造器有一个原型对象A,那么由这个构造器所创建的实例(Instance)都必然复制自A。

    “原型也是对象实例”是一个关键的性质,这是他与“类继承体系”最根本的区别。举例说明:“类”可以是一个内存块或者一段描述文本,而不必是一个有对象特性(例如可以调用方法或存取属性)的结构。

    二、 写复制

    上图说明每构造一个实例,都从原型中复制出一个实例,新的实例和原型占用了相同的内存空间,造成了内存空间的浪费,有没有策略让新实例和原型共用一个内存空间呢,这就是——写复制(一种欺骗系统的技术:操作系统中的动态链接库(DLL),它的内存区总是写时复制的),下面是机制图:

    image

    obj1和obj2都是指向原型的引用,这时它们并不占用内存,当需要写对象obj2的属性时,就会复制一个原型的映像出来,并使以后的操作指向该原型就行了。这种方法的优点就是只有第一次写操作的时候才会分配内存,以后就不会了,因为访问映像与访问原型的效率是一致的;缺点就是如果需要大量写操作的时候,就不太经济了。

    接下来,重点来了,JavaScript的实现机制是:仅当写某个实例的成员时,将成员的信息复制到实例映像中(需进一步论证),但当写对象属性时(如obj2.value=10),会产生一个包含属性的成员列表,此时,obj2仍然是一个指向原型的引用,并没有创建对象实例,所以不会占用内存,但obj2需要维护一张成员列表。机制图如下:image

    三、构造过程——从函数到构造器

    函数只有在被引用到原型时,才具有构造器特性。而且函数的原型永远都是一个标准的、系统内置的Object()构造器的实例(这也证明了‘js中一切皆对象’的说法),并且函数的原型的constructor属性值总是先被设置为当前函数。

    function myObj(){
    
    }
    
    //true,表明原型的构造器总是指向函数本身
    console.log(myObj.prototype.constructor == myObj);
    
    //删除该成员的值
    delete myObj.prototype.constructor;
    
    //false
    console.log(myObj.prototype.constructor == myObj);
    //true,删除操作使该成员指向父类原型中的值
    console.log(myObj.prototype.constructor == Object);
    console.log(myObj.prototype.constructor == new Object().constructor);

    结论:构造器的原型 prototype 总是来自于 new Object() 产生的实例

    由上可知:对象实例本质上就是一个指向构造器原型,并拥有一个属性列表(properties,或成员列表)的结构。

    进一步推论:“实例”之所以具有对象的某些性质,是因为他们拥有共同的最顶层原型Object.prototype(空的对象是一切对象的基础)。那么,js系统中都有什么预定义的属性与方法呢,见下表:

    表一:对象原型所具有的

    成员名 类型 分类
    toString function 动态语言
    toLocaleString function 动态语言
    valueOf function 动态语言
    constructor function 对象系统:构造
    propertyIsEnumerable function 对象系统:属性
    hasownProperty function 对象系统:属性
    isPrototypeOf function 对象系统:原型

    表二:构造器Object()所具有的

    成员名 类型 分类
    call function 函数式语言
    apply function 函数式语言
    caller function 函数式语言
    arguments object 动态语言
    length number 动态语言
    prototype object 对象系统:原型

    三、原型链的维护

    function myObj(){
    
    }
    function myObjEx(){
    
    }
    
    myObjEx.prototype = new myObj();
    
    obj1 = new myObjEx();
    obj2 = new myObjEx();

    结合以上结论(原型也是对象实例),画出上面代码的原型链:

    image

    该图说明,构造器通过prototype属性构建了一个原型链,而实例则通过constructor属性维护了一个原型链(其根源是构造器的原型的constructor属性指向了构造器本身)。

    然后,运行如下代码:

    object1 = new myObj();
    object2 = new myObjEx();
    
    //true
    console.log(object1.constructor == object2.constructor);

    没错,输出的结果是 true !

    object1和object2是两个不同的构造器的实例,可它们的constructor属性却指向了相同的构造器myObj。这是为什么呢?因为下面这行代码:

    /*
     *  等价于
     *  proto = new myObj(); //proto.constructor = myObj
     *  myObjEx.prototype = proto; //myObjEx.prototype.constructor = proto... = myObj
     */
    myObjEx.prototype = new myObj();

    既然知道了原因,那么我们就解决它

    很多人会这样做:

    //不完善的维护constructor
    myObjEx.prototype.constructor = myObjEx;

    这样就完美了吗?答案是否定滴,因为这样一来 constructor 就切断了原来的原型链,见下图:

    image

    当然,如果你不不想回溯原型链,就用上面的方法,否则,为了严谨一点,用下面的方法:

    //正确维护constructor,以便回溯原型链
    function myObjEx(){
    	this.constructor = myObjEx;
    	//or this.constructor = arguments.callee;
    }

    最后,展示一个经典的模型:动物王国

    //构造器
    function Animal() {} //动物类
    function Mammal() {} //哺乳类
    function Canine() {} //犬类
    function Dog() {} 
    function Cat() {}
    
    Animal.prototype.eat = function() {}
    
    //原型链
    Mammal.prototype = new Animal();
    Canine.prototype = new Mammal();
    Dog.prototype = new Canine();
    Cat.prototype = new Mammal();
    
    //示例函数
    function isAnimal(obj) {
    	return obj instanceof Animal;
    }
    //示例代码
    var dog = new Dog(),
    	cat = new Cat();
    //true
    console.log(isAnimal(dog));
    //true
    console.log('eat' in cat);
    //true
    console.log('eat' in dog);


    作者:狂流
    出处:http://www.cnblogs.com/kuangliu/
    欢迎转载,分享快乐! 如果觉得这篇文章对你有用,请抖抖小手,推荐一下!

  • 相关阅读:
    [SCOI2005]栅栏
    状压dp常用操作
    [SCOI2005]互不侵犯
    欧拉函数
    hdu5179 beautiful number
    hdu4460 Friend Chains
    exgcd详解
    hdu6468 zyb的面试
    hdu1978 How many ways
    hdu1312 Red and Black
  • 原文地址:https://www.cnblogs.com/kuangliu/p/4310871.html
Copyright © 2011-2022 走看看