高程三中的原型部分以及其中提到的构建模式

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <script>
        //写在前面:创建的每个函数都有一个属性:prototype,这个属性是一个指针,指向原型对象
        //原型对象的用途:包含可以由特定类型(Object,Array,Function以及各种自定义)的所有实例共享的属性与方法
        //或者说:prototype就是通过 调用构造函数构建的 那个对象实例 的原型对象 
        //这样就不必在构造函数中定义对象实例的信息,直接将这些信息添加到原型对象中即可    
        function Create(){};
        Create.prototype.name = "Harold";//通过Create.prototype来为构造函数的原型对象添加方法
        Create.prototype.method = function(){
            // alert("upup就完事了!");
        };
        var obj1 = new Create();
        var obj2 = new Create();
        obj1.method();
        console.log(obj1.method() == obj2.method() );//true
        //两个对象访问的是同一组属性与方法
        //如何理解原型对象?
        //无论何时,只要创建了一个函数,就会根据一组特定的规则,为该函数创建一个prototype属性(指向函数的原型对象)
        //默认情况下,所有原型对象都会获得一个constructor属性,这个属性是一个指向prototype属性所在函数的指针 
        // Person.prototype.construtor指向Person 而Person的prototype指向PersonPrototype 高程三P148
        //在创建了自定义的构造函数Func后,其原型对象默认只会取得constructor属性,其他均有Object继承而来
        //调用构造函数创建一个实例后,该实例的内部会包含一个指针,[[prototype]](属性_proto_)
        //这个连接/关系存在于实例与构造函数的原型对象间,而非实例与构造函数间
        //新实例的[[prototype]]/_proto_、构造函数的prototype属性均指向 Person.prototype
        //注意在上面的实例中我们可以直接调用person.method() 这就是通过查找对象属性的过程来实现的
        console.log(Create.prototype.isPrototypeOf(obj1));//true 用isPrototypeOf()方法判断 (这是原型对象的方法)
        console.log(Object.getPrototypeOf(obj1));//Object method: ƒ ()name: "Harold"constructor: ƒ Create()__proto__: Object
        ///这个方法返回[[prototype]] / _proto_的值(即obj1的原型对象),== Create.prototype 
        //注意:每当代码读取对象的某个属性,都会执行一次搜索,先在实例中搜索,如果没有找到,则继续前往[[prototype]]指针指向的
        //原型对象中搜索  这是多个对象实例共享原型所保存的属性和方法的基本原理

        //可以通过对象实例访问保存在原型中的值 但不能这样重写原型中的值  只会屏蔽 而不会覆盖 联系行37
        obj1.name = "budu";
        console.log(obj1.name);
        //使用delete操作符可以完全删除实例属性
        delete obj1.name;
        console.log(obj1.name);
        //hasOwnProperty()方法可以检验一个实例是否自己拥有给定的属性/方法
        console.log(obj1.hasOwnProperty("name"));//false
        //原型与in操作符:in操作符的两种使用方式,单独使用与在for-in循环中使用
        //单独使用时,in操作符会在能够通过对象访问给定的属性时返回true
        console.log("name" in obj1);//true 不论通过原型还是实例,只要能访问到就true
        //配合hasOwnProperty()方法 可以直接确定这个属性在实例上还是原型对象上
        function hasPrototypeProperty(object,attrname){
            return !object.hasOwnProperty(attrname) && (attrname in object);
            //T && T = T 不在实例上且能访问到-在对象上 真·hasPrototypeProperty  F && T = F
        }
        console.log(hasPrototypeProperty(obj1,"name"));//true
        obj1.name = "new";
        console.log(hasPrototypeProperty(obj1,"name"));//false
        //for-in循环 返回所有通过对象能访问的,可枚举的(enumerated属性为true),包括实例和原型
        //!屏蔽了原型中不可枚举属性的实例属性也会被返回,根据规定所有开发人员定义的属性都是可枚举的
        for (var i in Create.prototype){//返回这个对象所有可枚举的属性~ 
            console.log(i);
        }
        //Object.keys()方法取得
        // var keys = Object.keys(Create.prototype);
        var keys = Object.keys(obj1);//实例对象与原型对象返回的是不同的!
        console.log(keys);//返回的是数组
        // var attr = Object.getOwnPropertyNames(Create.prototype);
        var attr = Object.getOwnPropertyNames(obj1);//也是不同的~  可以得到所有的实例属性 无论是否可枚举
        console.log(attr);

        //更简单的原型语法
        // 用一个包含所有属性和方法的对象字面量来重写整个原型对象
        function NewCreate(){};
        NewCreate.prototype = {
            // constructor: "NewCreate",//可以特意这样设置  但这样会使得这个属性变得可枚举
            name:"linbudu",
            age:"21",
            sayit:function(){
                alert("If not me ,then who?")
            }
        }
        //注意,在这里prototype对象的constructor属性不再指向NewCreate了
        //在这里使用的语法本质上完全重写了该对象  该原型对象的construor属性指向Object构造函数
        var obj3 = new NewCreate();
        console.log(typeof obj3 );//object
        console.log(obj3 instanceof Object);//true  这些对象既是Object的实例(所有对象都继承自Object),也是NewCreate的实例
        console.log(obj3 instanceof NewCreate);
        Object.defineProperty(NewCreate.prototype,"constructor",{
            enumerable: false,
            value:NewCreate,
        })
        //原型的动态性  在原型中查找值的过程是一次搜索,因此对原型对象做的任何修改都能立即从实例上反映出来
        //即使先创建实例、后修改原型,其原因可以归结于实例与原型之间的松散连接关系,它们之间的连接只不过是一个指针而非一个副本
        //调用一个构造函数创建一个实例时,会为实例添加一个只想最初原型的[[prototype]]指针
        //如果把原型修改为另一个对象(重写),就相当于切断了构造函数与最初原型之间的联系
        function cre(){};
        var obj4 = new cre();
        cre.prototype = {
            constructor:"cc",
            name:"qqq",
            fruit:["apple","orange"]
        }
        // var obj4 = new cre();如果在重写原型之后再创建实例,那么引用的便是新的原型
        console.log(obj4.name);//undefined,obj4在重写原型之前就已经存在,它引用的仍然是最初的原型,name属性未定义
        console.log(cre.prototype.constructor);//cc  这说明重写原型对象切断了构造函数与最初原型之间的联系
        //注意 最初的原型对象依然存在,在重写前就存在的实例的[[prototype]]指针仍然指向它

        //牛皮的地方来了,通过原型对象不仅可以取得所有默认方法的引用,还可以定义新方法(甚至在原生的引用类型上!)
        // for(var a in String.prototype){
        //     console.log(a);
        // }
        var str = Object.keys(String);
        console.log(str);//返回一个空数组  估计是因为不可枚举
        var str1 = "qwerdfa";
        var str1_attr = Object.keys(str1);
        console.log(str1_attr);//返回0~6数组...

        String.prototype.newMethod = function(text){
            alert(text);
        }
        // str1.newMethod();//undefined
        // str1.newMethod(str1);//√

        //原型对象的问题:1.省略了为构造函数传递初始化参数,所有实例默认情况下会取得相同的属性值
        //2.对于包含基本值的属性,可以通过在实例上添加同名属性来屏蔽原型中的对应属性,达到修改值的目的
        //但对于引用类型值:见下面的例子
        var cre1 = new cre();
        var cre2 = new cre();

        cre1.fruit.push("pear");
        console.log(cre1.fruit);
        console.log(cre2.fruit);
        //修改了一个实例引用的数组,由于该数组存在于cre.PROTOTYPE中,修改也会通过其他的实例反映出来...

        //应当组合使用构造函数与原型模式
        function P(name,age,job){
            this.name = name;
            this.age = age;
            this.fruit = ["1","2","3"]
        }//在构造函数中定义实例属性
        // p.prototype = {
        //     constructor:P,
        //     shareMethod:function(){
        //         return true;
        //     }
        // }//在原型中定义共享的方法与属性
        //这样就不会发生上面的错误了,因为两个实例引用的是不同的数组

        // 动态原型模式
        // 在构造函数中加入
        if(typeof this.method != "function"){
            P.prototype.method = function(){
                alert(this.attrname);
            };
        }
        //这里只有当这个方法不存在的情况下才会为原型添加这个方法
        //这段代码只有初次调用构造函数时才会执行,此后该函数的原型已经完成初始化,同时这里的修改能后立刻在所有实例中得到反映
        //不能使用对象字面量重写原型,否则会切断已有的实例与新原型之间的联系(已有实例仍会指向旧原型)
        
        //寄生构造函数模式
        //创建一个函数,仅仅用来封装创建对象的代码,再返回新创建的对象
        function parasitic(q,w,e){
            var o = new Object();
            this.q = q;
            this.w = w;
            return o;
        }
        var para_obj = new parasitic(); //与工厂模式的区别在于把函数叫做构造函数并使用new操作符

        //该模式可用来为对象创建特殊的构造函数 如创建带有特殊额外方法的数组 但不能直接修改Array构造函数
        function SpString(){
            // var str = new String(arguments);
            var arr = new Array();
            arr.push.apply(arr,arguments);//在arr中以传入值为参数调用push函数
            arr.newMethod = function(){
                return this.join("|");
            };
            return arr;
            // str.newMethod = function(){
            //     return this.substring(0,3);
            // };
            // return str;
        }
        var test = new SpString("qqqqq","111","eee");
        console.log(test.newMethod());
        //构造函数返回的对象与构造函数及其原型属性之间无关(即与在构造函数的外部创建对象没有什么不同)
    </script>
</body>
</html>

 

posted @ 2019-03-07 09:50  林不渡  阅读(202)  评论(0编辑  收藏  举报